FutureTask源码
FutureTask可用于异步获取执行结果或取消执行任务的场景。通过传入Runnable或者Callable的任务给FutureTask,直接调用其run方法或者放入线程池执行,之后可以在外部通过FutureTask的get方法异步获取执行结果,因此,FutureTask非常适合用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。另外,FutureTask还可以确保即使调用了多次run方法,它都只会执行一次Runnable或者Callable任务,或者通过cancel取消FutureTask的执行等。
Callable中有一个call()函数,但是call()函数有返回值,而Runnable的run()函数不能将结果返回给客户程序。Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果、设置结果操作。get方法会阻塞,直到任务返回结果。
FutureTask是一个RunnableFuture<V>,RunnableFuture实现了Runnbale又实现了Futrue<V>这两个接口。
FutureTask实现Runnable,所以能丢到Thread执行。
FutureTask实现Runnable,所以能丢给ExcecuteService线程池执行,ExcecuteService线程池也是吧任务丢到Thread里面去的。
可以直接通过get()函数获取执行结果,该函数会阻塞,直到结果返回。
Callable 和 Future接口的区别 Callable规定的方法是call(),而Runnable规定的方法是run(). Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。 call()方法可抛出异常,而run()方法是不能抛出异常的只能内部消化。 运行Callable任务可拿到一个Future对象, Future表示异步计算的结果。 它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。 通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。 Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。 Callable和Future、FutureTask配合可以用来获取异步执行的结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。
线程池使用 FutureTask 时候需要注意的一点事,FutureTask 使用不当可能会造成调用线程一直阻塞,如何避免?
线程池使用 FutureTask 的时候如果拒绝策略设置为了 DiscardPolicy和DiscardOldestPolicy并且在被拒绝的任务的 Future 对象上调用无参 get 方法那么调用线程会一直被阻塞。
也就是说当 future 的状态 > COMPLETING 时候调用 get 方法才会返回,而明显 DiscardPolicy 策略在拒绝元素的时候并没有设置该 future 的状态,后面也没有其他机会可以设置该 future 的状态,所以 future 的状态一直是 NEW,所以一直不会返回,同理 DiscardOldestPolicy 策略也是这样的问题,最老的任务被淘汰时候没有设置被淘汰任务对于 future 的状态。
所以当使用 Future 的时候,尽量使用带超时时间的 get 方法,这样即使使用了 DiscardPolicy 拒绝策略也不至于一直等待,等待超时时间到了会自动返回的,如果非要使用不带参数的 get 方法则可以重写 DiscardPolicy 的拒绝策略在执行策略时候设置该 Future 的状态大于 COMPLETING 即可,但是查看 FutureTask 提供的方法发现只有 cancel 方法是 public 的并且可以设置 FutureTask 的状态大于 COMPLETING,重写拒绝策略具体代码可以如下:
public class MyRejectedExecutionHandler implements RejectedExecutionHandler { public void rejectedExecution(Runnable runnable, ThreadPoolExecutor threadPoolExecutor) { if (!threadPoolExecutor.isShutdown()) { if(null != runnable && runnable instanceof FutureTask){ ((FutureTask) runnable).cancel(true); } } } }
run():任务执行之后,设置s=COMPLETING=1(原状态不是NEW就不设置,被cancel()改变了),在设置s=NORMAL=2(正常完成)。任务执行异常之后,设置s=COMPLETING=1(原状态不是NEW就不设置,被cacnel()改变了),再设置s=EXCEPTIONAL=3(异常完成).
cancel(): false NEW0->CANCELLED4 true: NEW0 ->INTERRUPTING5->中断线程->INTERRUPTED6。
会先设置s=INTERRUPTING=5或者s=CANCELLED=4(原状态不是NEW就不设置,被run()改变了),设置成功之后,中断线程之后,再设置s=INTERRUPTED=6。(都表示异常了)。
cancel()方法的作用就是改变状态,中断异常,唤醒waiter。影响run()方法设置结果,影响get()awiatDone()获取结果抛出异常。
get():只有在s <= COMPLETING=1才去阻塞拿结果,s>1不阻塞直接去拿结果(结果是null,并且抛出异常)。
get()里面的awaitDone():等待结果时候,s > COMPLETING=1就返回(有可能取消或者异常了,得到的结果是null,并且抛出异常),s == COMPLETING=1就线程让步。
isCancelled():state >= CANCELLED=4
isDone():state != NEW=0
设置1:run()正常,run()异常
设置2:run()正常完成
设置3:run()异常完成
设置4:cancel(false)
设置5:cancel(true)第一步
设置6:cancel(true)中断后最后一步
public class BBD { public static void main(String[] args) throws Exception { Callable<String> callable = new Callable<String>() { @Override public String call() throws Exception { System.out.println("正在下载中..."); return "Hello World!"; } }; FutureTask1<String> futureTask = new FutureTask1<>(callable);// Callable变成FutureTask Thread t1 = new Thread(futureTask,"任务正常执行线程"); t1.start(); // new Thread(futureTask).start();//run()方法里面第一个CAS就return了 // new Thread(futureTask).start();//run()方法里面第一个CAS就return了 for (int i = 0; i < 3; i++) { new Thread(new Runnable() { @Override public void run() { try { futureTask.get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } }, "get()线程" + (i+1)).start(); new Thread(new Runnable() { @Override public void run() { try { futureTask.get(5L,TimeUnit.SECONDS); } catch (Exception e) { e.printStackTrace(); } } }, "get(5L)线程" + (i+1)).start(); } new Thread(new Runnable() { @Override public void run() { futureTask.cancel(true); } }, "cancel()线程").start(); boolean b = Thread.currentThread().isInterrupted();//cancel(true)会设置中断标记 //-------------------------------------------------------------------------------------------------- Runnable runnable = new Runnable() { @Override public void run() { System.out.println("正在下载中..."); } }; FutureTask1<String> runnableTask = new FutureTask1<>(runnable, "我是返回的结果"); new Thread(runnableTask).start(); System.out.println("从网络下载的结果为:" + runnableTask.get());//Runnable没有返回值,这里返回期望值。 } }