前言
在读别人写的代码的时候偶尔看到别人用futuretask实现多线程处理业务逻辑,因为不清楚它底层是如何做到的所以对于它的异步处理逻辑挺感兴趣的,因此当前这篇文章就是用来探索一下这个问题。
一. 测试代码
上测试代码。
二. FutureTask类
1.1类注释
该类是实现了RunnableFuture接口的类,而RunnableFuture接口又是继承了runnable与future接口。
上它的注释说明:
它就说能做到一部计算,主要就是对实现了callable与runnable接口的对象进行包装,然后提交给executor帮执行。
1.2 关键成员注释
1.callable就是用来保存从构造函数传进来的那个实现了callable接口的匿名子类
2. outcome就是用来保存callable接口的call方法返回值的对象。
3,runner保存运行当前callable的线程的引用。
4.WaitNode保存调用运行callable接口的线程的线程,就是说一般当线程调用task.get()方法的时候会挂起,然后等待任务线程执行完,并在返回结果的前一刻会将前者挂起的线程唤醒。
1.3 FutureTask构造函数
1.3.1 以callable接口为参数的构造器
可以看到如果传进来的对象是callable的子类对象就使用这个构造器,然后并将该callable引用地址赋值给this.callable.
1.3.2 以runnable接口为参数的构造器
如果是实现了runnable接口的对象的话,走下面的构造函数
砸门再康一下,Executors的callable方法。如下面代码看到的是将runnable对象包装成了一个适配器。
我们再追下去看这个适配器模式实现接口转换的代码---------其实就是在Executors类下创建一个静态内部类,然后RunnableAdapter实现了Callable接口,并在call方法下调用其持有从Runnable对象的task方法。这里的返回值就是result,随意result在与runnable进行传进来的时候目前看到是不参与计算的。/(ㄒoㄒ)/~~
1.4 FutureTask的run方法
这个run方法就是实现了runnable接口的run方法,核心方法呀,下面最主要的一行代码当然是c.call()方法,这里就执行了用户自己重写的call方法的业务逻辑。
1.4 FutureTask的set方法
这个方法就是用于唤醒已经挂起的另一个线程通知它接收计算的结果。
看outcome就是用来保存result的值。
1.5 FutureTask的finishCompletion()方法
调用该方法就是唤醒等待计算结果而被挂起的那个线程。
核心的两个局部成员:
1.WaitNode用于保存已经被挂起的线程的对象,然后当计算线程计算完结果后就便利这个链表寻找对应的线程进行唤醒。
2.另一个是LockSupport.unpark实现唤醒等待线程。
LockSupport的unpark方法还是委托了Unsafe对象实现线程唤醒。
1.6 FutureTask的get()方法
get方法的核心方法还是awaitDone方法
1.7 FutureTask的awaitDone方法
不难看到其下面是有个死循环,一直监听state状态,当反复遍历几次还没执行结束后就执行
就创建一个waiteNode节点并添加到链表中,然后调用LockSupport的park方法将当前线程挂起。
1.7 LockSupport的怕park方法
其实真正能将线程挂起的方法就是unsafe方法的park方法了。
总结
1.这里说的异步就是两个线程共同协作处理一个任务,一个线程负责计算,另一个线程负责获取当前结果然后继续向下处理,所以两个线程有序工作就需要一套通信机制,当要结果的线程没能等到计算的线程返回的结果的时候就挂起来,当负责计算的线程将结果计算出来后就将前者唤醒让它提取计算结果。
2.以runnable作为构造参数传进的futruetask其result也是作为参数被传进去保存,感觉没啥用一样。