前面两篇我们介绍了 线程的基本知识,Thread,Runnable,Callable,今天我们看看FutureTask的使用,为什么要介绍他呢,一方面他提供了线程执行结果的返回值,另外一方面和前面说的callable有些相通性
看看源码中FutureTask 的类继承关系:
public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V>
FutureTask类继承关系图
RunnableFuture接口中只定义了一个 run方法,
Runnable 接口我们都知道他只有一个 run方法,没有参数,没用返回值的方法
Future 接口提供了获取返回值的get方法,以及cancel方法。
Future接口 定义的5个方法
看看FutureTask的构造方法:
FutureTask的状态: 7种状态
/**
* The run state of this task, initially NEW. The run state
* transitions to a terminal state only in methods set,
* setException, and cancel. During completion, state may take on
* transient values of COMPLETING (while outcome is being set) or
* INTERRUPTING (only while interrupting the runner to satisfy a
* cancel(true)). Transitions from these intermediate to final
* states use cheaper ordered/lazy writes because values are unique
* and cannot be further modified.
*
* Possible state transitions:
* NEW -> COMPLETING -> NORMAL
* NEW -> COMPLETING -> EXCEPTIONAL
* NEW -> CANCELLED
* NEW -> INTERRUPTING -> INTERRUPTED
*/
private volatile int state;
private static final int NEW = 0;
private static final int COMPLETING = 1;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;
Talk is cheap,show me your code!!!
public class FutureTaskDemo {
static ExecutorService executor = Executors.newSingleThreadExecutor();
public static void main(String[] args) {
futureTaskDemo();
}
// 使用FutureTask,有返回值
private static void futureTaskDemo() {
Callable<Long> callable = new Callable<Long>() {
@Override
public Long call() throws Exception {
MyLog.printTimeAndThread("callable");
Thread.sleep(1000);
return 200L;
}
};
FutureTask<Long> futureTask = new FutureTask<Long>(callable);
// 1. 通过把FutureTask 对象作为Thread参数构建Thread对象,来使用FutureTask
/*Thread thread = new Thread(futureTask);
thread.start();
Long result = futureTask.get();
System.out.println("result:" + result);
*/
// 2. 通过把FutureTask 对象提交到线程池Executor中,来使用FutureTask
executor.submit(futureTask);
Long result = null;
try {
result = futureTask.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("result:" + result);
System.out.println("==============================");
// 2.1 当然上面这个 callable对象,相当于一个任务,可以直接丢入到executor线程池中处理, 如:
Future<?> future = executor.submit(callable);
try {
System.out.println("result:" + future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("==============================");
// 3. FutureTask 的构造方法,可以传入Runnable对象,以及一个返回值 对象,如:
MyData val = new MyData();
FutureTask<MyData> futureTask2 = new FutureTask<MyData>(new Runnable() {
@Override
public void run() {
MyLog.printTimeAndThread("futureTask2");
val.setValue(200);
}
}, val);
Future<MyData> future2 = (Future<MyData>) executor.submit(futureTask2);
MyData myData = null;
try {
myData = futureTask2.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("futureTask2 result:" + myData.getValue());
executor.shutdown();
}
}
class MyData {
Integer value;
public Integer getValue(){return value;}
public void setValue(Integer val){this.value = val;}
}
public class MyLog {
public static void sleepMillis(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void printTimeAndThread(String tag) {
String result = new StringJoiner("\t|\t")
.add(String.valueOf(System.currentTimeMillis()))
.add(String.valueOf(Thread.currentThread().getId()))
.add(Thread.currentThread().getName())
.add(tag)
.toString();
System.out.println(result);
}
}
运行结果:
1619077103371 | 12 | pool-1-thread-1 | callable
result:200
==============================
1619077104371 | 12 | pool-1-thread-1 | callable
result:200
==============================
1619077105374 | 12 | pool-1-thread-1 | futureTask2
futureTask2 result:200
这个运行结果,对照看下,应该很好理解就不多说了
总结下:
FutureTask,和Callable 有些类似
优点: 可以获取到线程运行完后方法的返回值。
这也是他的特性。但是要注意一旦调用get方法,调用线程就处于阻塞状态。
缺点:
A: FutureTask一旦调用get方法,需要捕获异常,这个异常处理就恶心了,代码就不'美'了
Future 接口中 get方法定义:
V get() throws InterruptedException, ExecutionException;
Callable 接口 也有这个问题,异常处理,而且他更恶心,抛出的是一个更宽泛的异常:
Callable接口中 call方法定义:
V call() throws Exception;
B: 还有就是使用FutureTask之前,要先构建一个Callable对象,有没有更简便的方式呢——更简洁的方式使用多线程,同时可以获取到线程的返回值,同时异常处理更优雅些。
答案是肯定有的,我们可以看下一篇吧,下一篇我们会介绍CompletableFuture的使用
Callable、Runable、Future、FutureTask简单总结:
Callable - 接口,只有call方法,有结果的同步行为;
Runnable - 接口,只有run方法,无结果的同步行为;
FutureTask - 实现类,异步封装Callable/Runnable,依赖Executor执行 ,执行的返回值是 Future对象, 通过Future来get异步执行的结果;