面试题:线程的创建方式有哪几种?
想都没想就答:有两种,一种是直接继承Thread类,一种是实现Runnable接口
面试官:Any more?
我:这·······
其实,还有一种叫做Callable,看起来跟Runnable有点像,但还是有区别的,我们还是直接上例子吧:
//第1种Thread
Thread thread=new Thread(){
@Override
public void run() {
super.run();
System.out.println("当前线程1:"+Thread.currentThread().getName());
}
};
thread.start();
//第2种Runnable
Runnable runnable=new Runnable() {
@Override
public void run() {
System.out.println("当前线程2:"+Thread.currentThread().getName());
}
};
new Thread(runnable).start();
//第3种Runnable
Callable<String> callable=new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("当前线程3:"+Thread.currentThread().getName());
Thread.sleep(2000);
return "你好吗?";
}
};
FutureTask<String> futureTask=new FutureTask<>(callable);
new Thread(futureTask).start();
try {
String s = futureTask.get();//阻塞线程,2秒后才返回结果
System.out.println("输出结果:"+ s);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
前面两种没什么好说的,我们来看看Runnable和Callable的区别:
1、最大的区别:Callable有返回值,Runnable没有返回值
2、Callable中的call方法允许 throws Exception,Runnable中的异常只能自己处理,不能上抛
我们重点来看看Callable,从上面的区别我们可以知道,其实引入了Callable最大的好处就是我们可以获取到线程执行的结果,上面的例子也正好说明这一点。
说到Callable,总免不了要说Future和FutureTask,我们来看看Future的源码:
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}
它是一个泛型接口,从它的方法我们可以直接,它提供了3个主要的功能:
1)判断任务是否完成;
2)能够中断任务;
3)能够获取任务执行结果。
而FutureTask是Future接口的一个唯一实现类,所以,我们上面用的就是FutureTask。
需要注意一点,拿上面例子来说吧: String s = futureTask.get(),这一步是阻塞线程的,一直到线程执行完毕返回结果。
网友评论