1919 - 任务接口(` Task ` ):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等
2020 - 任务队列(` TaskQueue ` ):用于存放没有处理的任务。提供一种缓冲机制。
2121
22-
2322- 使用线程池的好处:
24- - 减少在创建和销毁线程上所花的时间以及系统资源的开销
25- - 如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存以及”过度切换”。
26- - 提交相应速度
23+ - 降低资源消耗。通过重复利用减少在创建和销毁线程上所花的时间以及系统资源的开销
24+ - 提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,
25+ 还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存以及”过度切换”。
26+ - 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
2727
2828- 工作流程
2929 线程池的任务就在于负责这些线程的创建,销毁和任务处理参数传递、唤醒和等待。
3030 - 创建若干线程,置入线程池
3131 - 任务达到时,从线程池取空闲线程
3232 - 取得了空闲线程,立即进行任务处理
33- - 否则新建一个线程,并置入线程池,执行3
33+ - 否则新建一个线程,并置入线程池,并执行上一步
3434 - 如果创建失败或者线程池已满,根据设计策略选择返回错误或将任务置入处理队列,等待处理
3535 - 销毁线程池
3636
@@ -76,10 +76,10 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
7676
7777上面构造器中各参数的含义:
7878
79- - ` corePoolSize ` :核心池的大小 。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,
79+ - ` corePoolSize ` :用来表示线程池中的核心线程的数量,也可以称为可闲置的线程数量 。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,
8080除非调用了` prestartAllCoreThreads() ` 或者` prestartCoreThread() ` 方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建` corePoolSize ` 个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到` corePoolSize ` 后,就会把到达的任务放到缓存队列当中;
8181
82- - ` maximumPoolSize ` :线程池最大线程数, 这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;
82+ - ` maximumPoolSize ` :来表示线程池中最多能够创建的线程数量。 这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;
8383- ` keepAliveTime ` :表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于` corePoolSize ` 时,` keepAliveTime ` 才会起作用,直到线程池中的线程数不大于` corePoolSize ` ,即当线程池中的线程数大于` corePoolSize ` 时,如果一个线程空闲的时间达到` keepAliveTime ` ,则会终止,直到线程池中的线程数不超过` corePoolSize ` 。但是如果调用了` allowCoreThreadTimeOut(boolean) ` 方法,在线程池中的线程数不大于` corePoolSize ` 时,` keepAliveTime ` 参数也会起作用,直到线程池中的线程数为0;
8484- ` unit ` :参数` keepAliveTime ` 的时间单位,有7种取值,在` TimeUnit ` 类中有7种静态属性:
8585
@@ -100,7 +100,7 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
100100
101101- ` threadFactory ` :线程工厂,主要用来创建线程
102102- ` handler ` :表示当拒绝处理任务时的策略,有以下四种取值:
103-
103+
104104 - ` ThreadPoolExecutor.AbortPolicy ` :丢弃任务并抛出` RejectedExecutionException ` 异常
105105 - ` ThreadPoolExecutor.DiscardPolicy ` :也是丢弃任务,但是不抛出异常
106106 - ` ThreadPoolExecutor.DiscardOldestPolicy ` :丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
@@ -111,15 +111,18 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
111111线程池状态
112112---
113113
114+ ![ ] ( https://raw.githubusercontent.com/CharonChui/Pictures/master/thread_poll_state.png )
115+
116+
117+
114118` ThreadPoolExecutor ` 中定义了一个` volatile ` 变量,另外定义了几个` static final ` 变量表示线程池的各个状态:
115119
116- - ` volatile int runState; `
117- - ` static final int RUNNING = -1; ` 当创建线程池后,初始时,线程池处于 ` RUNNING ` 状态;
120+ - ` volatile int runState; ` 表示当前线程池的状态,它是一个 ` volatile ` 变量用来保证线程之间的可见性
121+ - ` static final int RUNNING = -1; ` 此状态下,线程池可以接受新的任务,也可以处理阻塞队列中的任务。执行shutdown()方法可进入待关闭(SHUTDOWN)状态,执行shutdownNow()方法可进入停止(STOP)状态。
118122- ` static final int SHUTDOWN = 0; ` 如果调用了` shutdown() ` 方法,则线程池处于` SHUTDOWN ` 状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕
119123- ` static final int STOP = 1; ` 如果调用了` shutdownNow() ` 方法,则线程池处于` STOP ` 状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务;
120- - ` static final int TIDYING = 2; ` 该状态表示线程池对线程进行整理优化;
121- - ` static final int TERMINATED = 3; ` 当线程池处于` SHUTDOWN ` 或` STOP ` 状态,并且所有工作线程已经销毁,任务缓存队列已经清空或执行结束后,线程池被设置为` TERMINATED ` 状态
122- ` runState ` 表示当前线程池的状态,它是一个` volatile ` 变量用来保证线程之间的可见性;
124+ - ` static final int TIDYING = 2; ` 此状态下,所有任务都已经执行完毕,且没有工作线程。执行terminated()方法进入终止(TERMINATED)状态。该状态表示线程池对线程进行整理优化;
125+ - ` static final int TERMINATED = 3; ` 当线程池处于` SHUTDOWN ` 或` STOP ` 状态,并且所有工作线程已经销毁,任务缓存队列已经清空或执行结束后,线程池被设置为` TERMINATED ` 状态。此状态下,线程池完全终止,并完成了所有资源的释放。
123126
124127
125128` Executors ` 类
@@ -170,6 +173,61 @@ public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
170173实际中,如果` Executors ` 提供的三个静态方法能满足要求,就尽量使用它提供的三个方法,因为自己去手动配置` ThreadPoolExecutor ` 的参数有点麻烦,要根据实际任务的类型和数量来进行配置。
171174
172175
176+
177+ ## 线程池执行状态
178+
179+ ![ ] ( https://raw.githubusercontent.com/CharonChui/Pictures/master/thread_poll_process.png )
180+
181+ 整个过程可以拆分成以下几个部分:
182+
183+ - 提交任务
184+
185+ 当向线程池提交一个新的任务时,线程池有三种处理情况,分别是:创建一个工作线程来执行该任务、将任务加入阻塞队列、拒绝该任务。
186+
187+ 提交任务的过程也可以拆分成以下几个部分:
188+
189+ 1 . 当工作线程数小于核心线程数时,直接创建新的核心工作线程。
190+ 2 . 当工作线程数大于核心线程数时,就需要尝试将任务添加到阻塞队列中去。
191+ 3 . 如果能够加入成功,说明队列还没满,那么就需要做以下的二次校验来保证添加进去的任务能够成功被执行。
192+ 4 . 验证当前线程池中的运行状态,如果是非RUNNING状态,则需要将任务从阻塞队列中移除,然后拒绝该任务。
193+ 5 . 验证当前线程池中的工作线程的个数,如果是0,则需要主动添加一个空工作线程来执行刚刚添加到阻塞队列中的任务。
194+ 6 . 如果加入失败,说明队列已经满了,这时就需要创建新的临时工作线程来执行任务。
195+ 7 . 如果创建成功,则直接执行该任务。
196+ 8 . 如果创建失败,说明工作线程数已经等于最大线程数了,只能拒绝该任务了。
197+
198+ - 创建工作线程
199+
200+ 创建工作线程需要做一系列的判断,需要确保当前线程池可以创建新的线程之后,才能创建。
201+
202+ 首先,当线程池的状态是SHUTDOWN或者STOP时,不能创建新的线程。
203+
204+ 其次,当线程工厂创建线程失败时,也不能创建新的线程。
205+
206+ 第三,拿当前工作线程的数量与核心线程数、最大线程数进行比较,如果前者大于后者的话,也不允许创建。除此之外,线程池会尝试通过CAS来自增工作线程的个数,如果自增成功了,则会创建新的工作线程,即Worker对象。
207+
208+ 然后加锁进行二次验证是否能够创建工作线程,如果最后创建成功,则会启动该工作线程。
209+
210+ - 启动工作线程
211+
212+ 当工作线程创建成功后,也就是Worker对象已经创建好了,这时就需要启动该工作线程,让线程开始干活了,Worker对象中关联着一个Thread,所以要启动工作线程的话,只要通过worker.thread.start()来启动该线程即可。
213+
214+ 启动完了之后,就会执行Worker对象的run方法,因为Worker实现了Runnable接口,所以本质上Worker也是一个线程。
215+
216+ 通过线程start开启之后就会调用到Runnable的run方法,在Worker对象的run方法中,调用了runWorker(this)方法,也就是把当前对象传递给了runWorker()方法,让它来执行。
217+
218+ - 获取任务并执行
219+
220+ 在runWorker方法被调用之后,就是执行具体的任务了,首先需要拿到一个可以执行的任务,而Worker对象中默认绑定了一个任务,如果该任务不为空的话,那么就是直接执行。
221+
222+ 执行完了之后,就会去阻塞队列中获取任务来执行。
223+
224+ 获取任务的过程则需要考虑当前工作线程的个数:
225+
226+ 1 . 如果工作线程数大于核心线程数,那么就需要通过poll(keepAliveTime, timeUnit)来获取,因为这时需要对闲置线程进行超时回收。
227+ 2 . 如果工作线程数小于等于核心线程数,那么就可以通过take()来获取了。因为这时所有的线程都是核心线程,不需要进行回收,前提是没有设置allowCoreThreadTimeOut(允许核心线程超时回收)为true。
228+
229+
230+
173231向线程池提交任务
174232---
175233
0 commit comments