|
1 | 1 | ### 为什么要使用线程池 |
2 | | -1. 创建、销毁线程需要消耗系统资源,线程池可以**复用已创建的线程**; |
3 | | -2. **控制并发的数量**,并发数量过多,可能会导致资源消耗过多,从而造成服务器崩溃;(主要原因) |
| 2 | +1. 创建、销毁线程需要消耗系统资源,线程池可以 **复用已创建的线程**; |
| 3 | +2. **控制并发的数量** ,并发数量过多,可能会导致资源消耗过多,从而造成服务器崩溃(主要原因); |
4 | 4 | 3. **可以对线程做统一管理**。 |
5 | 5 |
|
6 | 6 | ### 线程池的原理 |
@@ -52,9 +52,9 @@ public ThreadPoolExecutor(int corePoolSize, |
52 | 52 | - TimeUnit unit:keepAliveTime 的单位 |
53 | 53 | - BlockingQueue workQueue:阻塞队列,维护着等待执行的 Runnable 任务对象 |
54 | 54 | > 常用的阻塞队列: |
55 | | -> 1. LinkedBlockingQueue:链式阻塞队列,底层数据结构是链表,默认大小是Integer.MAX_VALUE,也可以指定大小。 |
| 55 | +> 1. LinkedBlockingQueue:链式阻塞队列,底层数据结构是链表,默认大小是 Integer.MAX_VALUE,也可以指定大小。 |
56 | 56 | > 2. ArrayBlockingQueue:数组阻塞队列,底层数据结构是数组,需要指定队列的大小。 |
57 | | -> 3. SynchronousQueue:同步队列,内部容量为0,每个put操作必须等待一个take操作,反之亦然。 |
| 57 | +> 3. SynchronousQueue:同步队列,内部容量为 0,每个 put 操作必须等待一个 take 操作,反之亦然。 |
58 | 58 | > 4. DelayQueue:延迟队列,该队列中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素 。 |
59 | 59 |
|
60 | 60 | - ThreadFactory threadFactory:创建线程的工厂 ,用于批量创建线程,统一在创建线程时设置一些参数,如是否守护线程、线程的优先级等。如果不指定,会新建一个默认的线程工厂。 |
@@ -124,16 +124,16 @@ public void execute(Runnable command) { |
124 | 124 | ``` |
125 | 125 |
|
126 | 126 | 总结一下处理流程: |
127 | | -1. 线程总数量 < corePoolSize,无论线程是否空闲,都会新建一个核心线程执行任务(让核心线程数量快速达到 corePoolSize,在核心线程数量 < corePoolSize 时)。**注意,这一步需要获得全局锁**。 |
| 127 | +1. 线程总数量 < corePoolSize,无论线程是否空闲,都会新建一个核心线程执行任务(让核心线程数量快速达到 corePoolSize,在核心线程数量 < corePoolSize 时)。 **注意,这一步需要获得全局锁**。 |
128 | 128 | 2. 线程总数量 >= corePoolSize 时,新来的线程任务会进入任务队列中等待,然后空闲的核心线程会依次去缓存队列中取任务来执行(**体现了线程复用**)。 |
129 | | -3. 当缓存队列满了,说明这个时候任务已经多到爆棚,需要一些“临时工”来执行这些任务了。于是会创建非核心线程去执行这个任务。**注意,这一步需要获得全局锁**。 |
| 129 | +3. 当缓存队列满了,说明这个时候任务已经多到爆棚,需要一些“临时工”来执行这些任务了。于是会创建非核心线程去执行这个任务。 **注意,这一步需要获得全局锁**。 |
130 | 130 | 4. 缓存队列满了, 且总线程数达到了 maximumPoolSize,则会采取上面提到的拒绝策略进行处理。 |
131 | | - |
| 131 | + |
132 | 132 |
|
133 | 133 | #### ThreadPoolExecutor 如何做到线程复用的? |
134 | 134 | ThreadPoolExecutor 在创建线程时,会将线程封装成工作线程 worker,并放入工作线程组中,然后这个 worker 反复从阻塞队列中拿任务去执行。 |
135 | 135 |
|
136 | | -首先去执行创建这个 worker 时就有的任务,当执行完这个任务后,worker 的生命周期并没有结束,在 while 循环中,worker 会不断地调用 getTask 方法从阻塞队列中获取任务然后调用 task.run() 执行任务,从而达到复用线程的目的。只要 getTask 方法不返回 null,此线程就不会退出。 |
| 136 | +首先去执行创建这个 worker 时就有的任务,当执行完这个任务后,worker 的生命周期并没有结束,在 while 循环中,worker 会不断地调用 getTask 方法从阻塞队列中获取任务然后调用 task.run() 执行任务,从而达到复用线程的目的。只要 getTask 方法不返回 null,此线程就不会退出。 |
137 | 137 |
|
138 | 138 | 核心线程的会一直卡在 workQueue.take 方法,被阻塞并挂起,不会占用 CPU 资源,直到拿到 Runnable 然后返回(当然如果 allowCoreThreadTimeOut 设置为 true,那么核心线程就会去调用 poll 方法,因为 poll 可能会返回 null,所以这时候核心线程满足超时条件也会被销毁)。 |
139 | 139 |
|
@@ -168,4 +168,3 @@ CacheThreadPool 的运行流程如下: |
168 | 168 | #### newScheduleThreadPool |
169 | 169 | 创建一个定长线程池,支持定时及周期性任务执行。 |
170 | 170 |
|
171 | | - |
0 commit comments