概述
之前在Java随笔-线程创建中指出线程创建方式只有两种:Thread扩展类、Runnable实现类,并没有把Callable与FutureTask算成一种,也没有解释原因,本文就说明一下为什么通过Callable与FutureTask创建线程不能算一种。
使用
public class CallableDemo {
private static class CallableUse implements Callable<Integer> {
@Override
public Integer call() throws Exception {
// 再次输出当前线程名称
System.out.println(Thread.currentThread().getName());
System.out.println("这是callable的实现");
Thread.sleep(1000);
System.out.println("线程睡了1000ms后callable的实现");
return 11;
}
}
public static void main(String[] args) {
// 输出当前线程名称
System.out.println(Thread.currentThread().getName());
CallableUse callableUse = new CallableUse();
FutureTask<Integer> futureTask = new FutureTask<>(callableUse);
// 开启线程
new Thread(futureTask).start();
try {
// 输出返回值
System.out.println(futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
结果输出:
main
Thread-0
这是callable的实现
线程睡了1000ms后callable的实现
11
结束
创建步骤:
- 创建Callable实现类,并指定泛型,即返回值类型。
- 以Callable实现类实例为参数创建FutureTask实例,同样需要指定泛型。
- 将FutureTask实例作为Thread参数开启线程。
优势
- 通过Callable与FutureTask创建线程,然后调用futureTask.get()可以获取Callable实现类的返回值。
使用Thread和Runnable时,run方法是没有返回值的,若是需要拿到结果还需通过其他方式,而Callable的call() 是可以直接返回结果。 - 异步。通过futureTask.get()获取返回值时,即使call()中 Thread.sleep(1000),依然会等所有事情处理完成后再返回结果。此时会该线程会阻塞主线程,等该线程完成后,主线程才会继续执行,而Thread和Runnable线程中阻塞后并不影响主线程执行。
不算新的启动线程方式原因
通过Callable与FutureTask启动线程是Runnable启动线程的一种。
- Callable和Runnable基本一致。
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface {@code Runnable} is used
* to create a thread, starting the thread causes the object's
* {@code run} method to be called in that separately executing
* thread.
* <p>
* The general contract of the method {@code run} is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
- FutureTask是Runnable的一种。
首先FutureTask实现类RunnableFuture。
public class FutureTask<V> implements RunnableFuture<V> {...}
然后RunnableFuture实现了Runnable。
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
注:Java中接口可以多继承。
- Thread构造中没有FutureTask参数。
Thread构造有很多,但是没有以FutureTask为参数的,但使用时可以像使用Runnable那样使用,故FutureTask是Runnable是一种。
综上所诉,通过Callable与FutureTask创建线程是实现Runnable接口创建线程中的一种方式,所以通过Callable与FutureTask创建线程不算是新的创建线程方式。