目录
Fork/Join框架
运行流程
工作窃取算法
核心组件
ThreadPoolExecutor
处理过程
线程池创建
线程池状态
工作线程
线程工厂
线程池调度过程
核心方法execute方法
工作线程的执行
工作线程的清理
任务的获取
线程池拒绝策略
线程池关闭
合理配置线程池
线程池监控
Executor框架
Excutors下的几种线程池
ScheduledThreadPoolExecutor
构造方法
delayedExecute方法
Future核心组件:
FutureTask任务的7种状态
主要属性
任务的运行
任务的取消
结果获取
工作窃取(work-stealing)算法是指某个线程从其他队列里窃取任务来执行。
假如需要做一个比较大的任务,可以把这个任务分割为若干互不依赖的子任务,为了减少线程间的竞争,把这些子任务分别放到不同的队列里,并为每个队列创建一个单独的线程来执行队列里的任务,线程和队列一一对应。
比如A线程负责处理A队列里的任务。但是,有的线程会先把自己队列里的任务干完,而其他线程对应的队列里还有任务等待处理。干完活的线程与其等着,不如去帮其他线程干活,于是它就去其他线程的队列里窃取一个任务来执行,而这时它们会访问同一个队列。
为了减少窃取任务线程和被窃取任务线程之间的竞争,通常会使用双端队列,如LinkedBlockingDeque,被窃取任务线程永远从双端队列的头部拿任务执行,而窃取任务的线程永远从双端队列的尾部拿任务执行。
工作窃取算法的优点:充分利用线程进行并行计算,减少了线程间的竞争。
工作窃取算法的缺点:在某些情况下还是存在竞争,比如双端队列里只有一个任务时。并且该算法会消耗了更多的系统资源,比如创建多个线程和多个双端队列。
转载两篇分析源码博文
Java多线程进阶(四三)—— J.U.C之executors框架:Fork/Join框架(1) 原理
Java多线程进阶(四三)—— J.U.C之executors框架:Fork/Join框架(2)实现
在开发过程中,合理地使用线程池能够带来3个好处:
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源, 还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。
ThreadPoolExecutor一共提供了4种构造器,但其它三种内部其实都调用了下面的构造器。
ThreadPoolExecutor一共定义了5种线程池状态:
其中addWorker(null, false),当将任务成功添加到队列后,如果此时的工作线程数为0,就会执行这段代码。
将任务添加到队列后,需要判断工作线程数是否为0,如果是0那么就必须新建一个空任务的工作线程,将来在某一时刻它会去队列取任务执行,否则没有工作线程的话,该队列中的任务永远不会被执行。
addWorker方法
addWorkerFailed方法
其中if判断语句作用是:
工作线程需要清理有两种情况:
processWorkerExit的作用就是将该退出的工作线程清理掉,然后判断线程池是否需要终止。
getTask方法的主要作用就是:通过自旋,不断地尝试从阻塞队列中获取一个任务,如果获取失败则返回null。
线程池有两种情况下会执行拒绝策略:
AbortPolicy(默认)
DiscardPolicy
DiscardOldestPolicy
CallerRunsPolicy
原理:遍历线程池中的工作线程,调用interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。
只要调用了这两个关闭方法中的任意一个,isShutdown方法就会返回true。当所有的任务都已关闭后,才表示线程池关闭成功,这时调用isTerminaed方法会返回true。
合理配置线程池,必须首先分析任务特性,可以从以下几个角度来分析:
性质不同的任务可以用不同规模的线程池分开处理。CPU密集型任务应配置尽可能小的线程,如配置Ncpu+1个线程的线程池。
由于IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,如2*Ncpu。
混合型的任务,如果可以拆分,将其拆分成一个CPU密集型任务和一个IO密集型任务,只要这两个任务执行的时间相差不是太大,那么分解后执行的吞吐量将高于串行执行的吞吐量。如果这两个任务执行时间相差太大,则没必要进行分解。
可以通过Runtime.getRuntime().availableProcessors()方法获得当前设备的CPU个数。
扩展线程池进行监控:可以通过继承线程池来自定义线程池,重写线程池的 beforeExecute、afterExecute和terminated 方法,在任务执行前、执行后和线程池关闭前执行一些代码来进行监控。
Executor框架的两级调度模型:
即应用程序通过Executor框架控制上层的调度,下层的调度由操作系统内核控制。
Executor框架的使用示意图:
固定线程数的线程池:空闲存活时间为0L,故多余的空闲线程会立即被终止。无界队列(队列过长容易导致OOM)。
单个线程的线程池:空闲存活时间为0L,故多余的空闲线程会立即被终止。无界队列(队列过长容易导致OOM)。
可缓存的线程池:如果主线程提交任务的速度高于线程处理任务的速度,CachedThreadPool会不断创建新线程。极端情况下, CachedThreadPool会因为创建过多线程而耗尽CPU和内存资源导致OOM。
可周期调度的线程池:
JDK1.7之前,FutureTask通过内部类实现了AQS框架来实现功能。 JDK1.7及以后,则改变为直接通过Unsafe类CAS操作state状态字段来进行同步。
该图转自https://segmentfault.com/a/1190000016767676#articleHeader5
finishCompletion方法Removes and signals all waiting threads:
参考 《Java并发编程的艺术》
https://segmentfault.com/blog/ressmix_multithread