1. callable runnable future
Callable,有结果的同步行为,比如做蛋糕,产生蛋糕
Runnable,无结果的同步行为,比如喝牛奶,仅仅就是喝
Future,异步封装Callable/Runnable,比如委托给师傅(其他线程)去做糕点
CompletableFuture,封装Future,使其拥有回调功能,比如让师傅主动告诉我蛋糕做好了
2. future的缺点
在java8以前,我们使用java的多线程编程,一般是通过Runnable中的run方法来完成,这种方式,有个很明显的缺点,就是,没有返回值,这时候,大家可能会去尝试使用Callable中的call方法,然后用Future返回结果。
当调用future的get()方法时,当前主线程是堵塞的,这好像并不是我们想看到的,另一种获取返回结果的方式是先轮询,可以调用isDone,等完成再获取,但这也不能让我们满意. 比如:很多个异步线程执行时间可能不一致,我的主线程业务不能一直等着,这时候我可能会想要只等最快的线程执行完或者最重要的那个任务执行完,亦或者我只等1秒钟,至于没返回结果的线程我就用默认值代替
3. 设计与实现
从CompletableFuture的使用方法可以看出,CompletableFuture主要是通过回调的方式实现异步编程,解决Future在使用过程中需要阻塞的问题。因此如何设计线程安全的高性能回调机制是CompletableFuture需要解决的核心问题。
设计模式类似观察者模式:CompletableFuture是发布者,使用链表保存观察者Completion。分析代码后得知CompletableFuture的postComplete方法是通知方法,用于在CompletableFuture完成时通知观察者,发送订阅的数据。Completion的tryFire方法用于处理CompletableFuture发布的结果。
4. 整体调用
5. 算法实现
如果算法中一个线程的失败或挂起没有导致其它的线程失败或挂起,我们就说这个算法是非阻塞(nonblocking)的。非阻塞算法可以让多个线程在竞争相同资源时不会发生阻塞。如果算法中的每一步都至少有一个线程可以继续执行,我们就说这个算法是无锁(lock-free)的。无锁算法对死锁和其他活跃度问题具有免疫性。CompletableFuture使用了名为Treiber Stack的一种用cas操作解决并发冲突,实现非阻塞无锁并发栈的算法。
6. thenApply为例
后续任务入栈等待通知,通知必须得在入栈后才发送过来,否则可能导致永远等不到通知