2015年7月28日星期二

Java线程池相关-ForkJoinPool

当一个问题被拆分成两个或者多个子问题的时候,需要启动多个子线程去执行,在必要的情况下会迭代的依次启动下去。这里就产生了一些线程之间的依赖,这个大的问题需要等待它的子问题线程返回,因此需要某些机制来保证他们的同步。ThreadPoolExecutor默认使用的线程池是期望他们所有执行的任务都是不相关的,可以尽可能的并行执行。

ForkJoinPool有一个特点是work stealing。每个工作线程都有自己的工作队列,这是使用deque来实现的。当一个任务划分一个新线程时,它将自己推到 deque 的头部。当一个任务执行与另一个未完成任务的合并操作时,它会将另一个任务推到队列头部并执行,而不会休眠以等待另一任务完成(像 Thread.join() 的操作一样)。当线程的任务队列为空,它将尝试从另一个线程的 deque 的尾部 窃取另一个任务。如果我们用传统的ThreadPoolExecutor则比较难用上work stealing的技术。
Fork/Join 模式有自己的适用范围。如果一个应用能被分解成多个子任务,并且组合多个子任务的结果就能够获得最终的答案,那么这个应用就适合用 Fork/Join 模式来解决。



ForkJoinPool核心的添加是新的ForkJoinPool执行者,专门执行实现了ForkJoinTask接口的实例。ForkJoinTask对象支持创建子任务来等待子任务完成。有了这些清晰的语义,当一个任务正在等待另一个任务完成并且有待执行的任务时,executor就能够通过”偷取”任务,在内部的线程池里分发任务。

ForkJoinTask对象主要有两个重要的方法:
  • fork()方法允许ForkJoinTask任务异步执行,也允许一个新的ForkJoinTask从存在的ForkJoinTask中被启动。
  • 反过来, join()方法允许一个ForkJoinTask等待另一个ForkJoinTask执行完成。
如图所示,通过fork()和join()实现任务间的相互合作。注意fork()和join()方法名称不应该与POSIX中的进程能够复制自己的过程相混淆。fork()只会让ForkJoinPool调度一个新的任务,而不会创建子虚拟机。


有两种类型的ForkJoinTask的定义:
  • RecursiveAction的实例代表执行没有返回结果。
  • 相反,RecursiveTask会有返回值。

通常,RecursiveTask是首选的,因为大部分分而治之的算法会在数据集上计算后返回结果。对于任务的执行,不同的同步和异步选项是可选的,这样就可以实现复杂的模式。
注意点:ForkJoinTask对应的fork/join任务应该是纯内存算法,而没有I/O操作。此外,应该尽可能避免通过共享状态来进行任务间的通信,因为这通常意味着加锁会被执行。理想情况下,仅当一个任务fork另一个任务或一个任务join另一个任务时才进行任务通信。

ForkJoinPool系统介绍:http://ifeve.com/fork-and-join-java/
Java线程池概述:http://blog.csdn.net/dm_vincent/article/details/39505977
详解介绍了线程池的线程数目如何设置以及普通线程池和ForkJoinPool的区别的使用场景。

ForkJoinPool简单源码解析:http://blog.csdn.net/aesop_wubo/article/details/10300273

ForkJoinPool介绍:
http://www.ibm.com/developerworks/cn/java/j-jtp11137.html
http://www.ibm.com/developerworks/cn/java/j-lo-forkjoin/index.html

没有评论:

发表评论