├── src
├── main
│ ├── resources
│ │ └── application.yml
│ └── java
│ │ └── com
│ │ └── fyh
│ │ └── threadpool
│ │ ├── ThreadPoolApplication.java
│ │ └── main
│ │ └── StretchableThreadPool.java
└── test
│ └── java
│ └── com
│ └── fyh
│ └── threadpool
│ └── ThreadPoolApplicationTest.java
├── .gitattributes
├── img
├── task-arrangemnt.png
└── task-arrangement.svg
├── .gitignore
├── LICENSE
├── pom.xml
└── README.md
/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/img/task-arrangemnt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FYH620/ThreadPool/HEAD/img/task-arrangemnt.png
--------------------------------------------------------------------------------
/src/main/java/com/fyh/threadpool/ThreadPoolApplication.java:
--------------------------------------------------------------------------------
1 | package com.fyh.threadpool;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class ThreadPoolApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(ThreadPoolApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### Maven template
2 | /target/
3 | pom.xml.tag
4 | pom.xml.releaseBackup
5 | pom.xml.versionsBackup
6 | pom.xml.next
7 | release.properties
8 | dependency-reduced-pom.xml
9 | buildNumber.properties
10 | .mvn/timing.properties
11 |
12 | /.idea/
13 |
14 | ## File-based project format:
15 | *.iws
16 | *.iml
17 | *.ipr
18 |
19 | ## Plugin-specific files:
20 |
21 | # IntelliJ
22 | /out/
23 |
24 | # mpeltonen/sbt-idea plugin
25 | .idea_modules/
26 |
27 | # JIRA plugin
28 | atlassian-ide-plugin.xml
29 |
30 | # Crashlytics plugin (for Android Studio and IntelliJ)
31 | com_crashlytics_export_strings.xml
32 | crashlytics.properties
33 | crashlytics-build.properties
34 | fabric.properties
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 FYH620
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/test/java/com/fyh/threadpool/ThreadPoolApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.fyh.threadpool;
2 |
3 | import com.fyh.threadpool.main.StretchableThreadPool;
4 | import lombok.AllArgsConstructor;
5 | import lombok.Data;
6 | import lombok.extern.slf4j.Slf4j;
7 | import org.junit.jupiter.api.Test;
8 | import org.springframework.boot.test.context.SpringBootTest;
9 |
10 | import java.util.concurrent.LinkedBlockingDeque;
11 |
12 | @Slf4j
13 | @SpringBootTest
14 | class ThreadPoolApplicationTest {
15 |
16 | @Test
17 | public void testStretchablePool() throws InterruptedException {
18 | StretchableThreadPool pool = new StretchableThreadPool(5, 10,
19 | 3000, new LinkedBlockingDeque<>());
20 |
21 | for (int i = 0; i < 30; i++) {
22 | pool.createNewWork(new ActualWork(i + 1));
23 | Thread.sleep(100);
24 | }
25 |
26 | Thread.sleep(30 * 1000);
27 | log.info("all work finished");
28 | }
29 | }
30 |
31 |
32 | @Data
33 | @Slf4j
34 | @AllArgsConstructor
35 | class ActualWork implements Runnable {
36 | private Integer workId;
37 |
38 | @Override
39 | public void run() {
40 | // 1.工作时打印当前任务的ID号
41 | log.info("work {} run in the thread pool", workId);
42 |
43 | // 2.当前线程睡上5s(模拟当前线程处理该任务5s)
44 | try {
45 | Thread.sleep(5 * 1000);
46 | } catch (InterruptedException e) {
47 | log.error(e.getMessage());
48 | }
49 |
50 | // 3.当前任务结束
51 | log.info("work {} end", workId);
52 | }
53 | }
54 |
55 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | 4.0.0
6 |
7 | org.springframework.boot
8 | spring-boot-starter-parent
9 | 2.7.7
10 |
11 |
12 |
13 | com.fyh
14 | ThreadPool
15 | 0.0.1-SNAPSHOT
16 |
17 |
18 | 11
19 |
20 |
21 |
22 |
23 | org.springframework.boot
24 | spring-boot-starter
25 |
26 |
27 |
28 | org.springframework.boot
29 | spring-boot-devtools
30 | runtime
31 | true
32 |
33 |
34 |
35 | org.projectlombok
36 | lombok
37 | true
38 |
39 |
40 |
41 | org.springframework.boot
42 | spring-boot-starter-test
43 | test
44 |
45 |
46 |
47 |
48 |
49 |
50 | org.springframework.boot
51 | spring-boot-maven-plugin
52 |
53 |
54 |
55 | org.projectlombok
56 | lombok
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/src/main/java/com/fyh/threadpool/main/StretchableThreadPool.java:
--------------------------------------------------------------------------------
1 | package com.fyh.threadpool.main;
2 |
3 | import lombok.Data;
4 | import lombok.extern.slf4j.Slf4j;
5 |
6 | import java.util.concurrent.BlockingQueue;
7 | import java.util.concurrent.TimeUnit;
8 | import java.util.concurrent.atomic.AtomicInteger;
9 | import java.util.concurrent.locks.ReentrantLock;
10 |
11 | @Slf4j
12 | public class StretchableThreadPool {
13 | /**
14 | * 堵塞任务队列
15 | */
16 | private BlockingQueue workQueue;
17 |
18 | /**
19 | * 一个线程等待多少毫秒仍然没有任务就自杀
20 | */
21 | private long maxWaitMilliseconds;
22 |
23 | /**
24 | * 线程核心数
25 | */
26 | private int coreThreadCount;
27 |
28 | /**
29 | * 最大线程数
30 | */
31 | private int maxThreadCount;
32 |
33 |
34 | /**
35 | * 当前线程数量
36 | */
37 | private AtomicInteger nowThreadCount;
38 |
39 | /**
40 | * 线程名称递增ID号
41 | */
42 | private AtomicInteger threadIncrementThreadName;
43 |
44 | /**
45 | * 线程锁用来锁住线程销毁,避免销毁的线程超出预期
46 | */
47 | private ReentrantLock lock;
48 |
49 | /**
50 | * @param coreThreadCount 核心线程数量
51 | * @param maxThreadCount 最大线程数量
52 | * @param maxWaitMilliseconds 线程等待多长时间没有任务后自杀
53 | * @param workQueue 阻塞队列
54 | */
55 | public StretchableThreadPool(int coreThreadCount, int maxThreadCount, long maxWaitMilliseconds, BlockingQueue workQueue) {
56 | if (coreThreadCount > maxThreadCount) {
57 | log.error("核心线程数量不能大于最大线程数量");
58 | }
59 |
60 | // 初始化线程池参数
61 | this.coreThreadCount = coreThreadCount;
62 | this.maxThreadCount = maxThreadCount;
63 | this.maxWaitMilliseconds = maxWaitMilliseconds;
64 | this.workQueue = workQueue;
65 |
66 | // 初始化锁和线程池中的记录变量
67 | this.nowThreadCount = new AtomicInteger(0);
68 | this.threadIncrementThreadName = new AtomicInteger(0);
69 | this.lock = new ReentrantLock();
70 |
71 | // 500毫秒判断一次是否需要扩容线程(单独开一个监控线程用于监控扩容条件)
72 | this.startThreadsToExpandCapacity(500);
73 |
74 | // 创建核心线程数量的线程用于执行真正要执行的任务
75 | for (int i = 0; i < coreThreadCount; ++i) {
76 | this.createNewThread();
77 | }
78 | log.info("thread pool created, now has {} threads", nowThreadCount.get());
79 | }
80 |
81 |
82 | /**
83 | * @param work:真正要执行的任务对象(需要重写Runnable接口中的run方法为自己想要执行的)
84 | */
85 | public void createNewWork(Runnable work) {
86 | workQueue.add(work);
87 | log.info("new work added for function {}", work);
88 | }
89 |
90 |
91 | /**
92 | * 线程池中每个线程真正在执行的方法
93 | */
94 | private void workerFunction() {
95 | while (true) {
96 | try {
97 | // 尝试获取任务并等待,如果等待的时间超过设定的时间没有任务就需要判断是否销毁线程了
98 | Runnable workToDo = workQueue.poll(maxWaitMilliseconds, TimeUnit.MILLISECONDS);
99 |
100 | // 等待超时的情况(没取到任务)
101 | if (workToDo == null) {
102 |
103 | // 双重校验自杀条件,确保安全自杀,线程池线程数量不会小于核心线程数
104 | if (nowThreadCount.get() > coreThreadCount) {
105 | // 尝试去加锁,如果加锁失败那就再次循环不去等待,防止同一时间内大量线程被销毁
106 | boolean lockedSuccess = lock.tryLock();
107 |
108 | // 加锁成功,再次判断是否满足自杀条件
109 | if (lockedSuccess) {
110 |
111 | // 满足自杀条件,线程自杀,解锁
112 | if (nowThreadCount.get() > coreThreadCount) {
113 | log.info("* thread {} end, left {} threads in pool", Thread.currentThread().getName(), nowThreadCount.decrementAndGet());
114 | lock.unlock();
115 | break;
116 | }
117 |
118 | // 不满足自杀条件就解锁继续循环
119 | lock.unlock();
120 | }
121 | }
122 | continue;
123 | }
124 |
125 | // 等待没有超时(取到了任务就开始执行)
126 | log.info("thread {} work for function: {}}", Thread.currentThread().getName(), workToDo);
127 | workToDo.run();
128 |
129 | } catch (Exception e) {
130 | log.error(e.getMessage());
131 | }
132 | }
133 | }
134 |
135 | private void createNewThread() {
136 | nowThreadCount.incrementAndGet();
137 | Thread t = new Thread(this::workerFunction, String.valueOf(threadIncrementThreadName.incrementAndGet()));
138 | t.start();
139 | }
140 |
141 | // 中控线程
142 | /**
143 | * 当一个任务等待waitMilliseconds时间后没有被消费且等待后任务队列容量超过线程池最大数量后再扩容
144 | *
145 | * @param waitMilliseconds 等待毫秒数
146 | */
147 | private void startThreadsToExpandCapacity(long waitMilliseconds) {
148 | new Thread(() -> {
149 | while (true) {
150 | try {
151 | // 当前线程先等一等看看任务会不会被迅速消费
152 | TimeUnit.MILLISECONDS.sleep(waitMilliseconds);
153 | // 获取等待后的任务容量
154 | int afterSize = workQueue.size();
155 | // 如果可以扩容线程池则扩容
156 | if (afterSize > this.maxThreadCount) {
157 | if (this.nowThreadCount.get() + 1 <= this.maxThreadCount) {
158 | this.createNewThread();
159 | }
160 | }
161 | } catch (Exception e) {
162 | log.error(e.getMessage());
163 | }
164 | }
165 | }).start();
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## 需要原始版本的请切换到 initial-implement 分支,最新的 main 分支是来自 [supermarketss](https://github.com/supermarketss) 的第二种伸缩策略的实现,API 有所改变,更新规则与使用方法如下
2 |
3 | - 使用了 JUC 线程安全数据结构,**性能更优**
4 | - 扩充算法的新实现:专门再开一个**监控线程**在循环内等 500ms 检测一下任务队列任务数量是否比线程池最大线程数目多,且满足当前线程数量不超过最大线程数量就一次扩充一个线程
5 |
6 | - 使用方法
7 |
8 | ```java
9 | @Slf4j
10 | @SpringBootTest
11 | class ThreadPoolApplicationTest {
12 |
13 | @Test
14 | public void testStretchablePool() throws InterruptedException {
15 | // 1.初始化线程池(线程池在这里需要传入四个初始化参数)
16 | // (1):coreThreadCount 线程池核心线程数目
17 | // (2):maxThreadCount 线程池最大线程数目
18 | // (3):maxWaitSeconds 当前线程等待maxWaitSeconds毫秒后仍然接收不到新来的任务就会自杀
19 | // (4):第四个参数需要传入一个并发队列接口的子实现类对象
20 | StretchableThreadPool pool = new StretchableThreadPool(5, 10,
21 | 3000, new LinkedBlockingDeque<>());
22 |
23 | // 2.调用createNewWork方法传入实现了Runnable接口的对象
24 | pool.createNewWork(new ActualWork(10));
25 | Thread.sleep(5 * 1000);
26 | log.info("all work finished");
27 | }
28 | }
29 |
30 | @Data
31 | @Slf4j
32 | @AllArgsConstructor
33 | class ActualWork implements Runnable {
34 | private Integer workId;
35 |
36 | @Override
37 | public void run() {
38 | // 1.工作时打印当前任务的ID号
39 | log.info("work {} run in the thread pool", workId);
40 |
41 | // 2.当前线程睡上5s(模拟当前线程处理该任务5s)
42 | try {
43 | Thread.sleep(5 * 1000);
44 | } catch (InterruptedException e) {
45 | log.error(e.getMessage());
46 | }
47 |
48 | // 3.当前任务结束
49 | log.info("work {} end", workId);
50 | }
51 | }
52 | ```
53 |
54 | ## Java 可伸缩线程池最初版本 (StretchableThreadPool)
55 |
56 | ### 🛠 食用方法
57 |
58 | ```Java
59 | // 详情见项目的测试类
60 | public void howToUse() {
61 | // 1.创建可伸缩线程池对象
62 | StretchableThreadPool pool = new StretchableThreadPool();
63 |
64 | // 2.初始化线程池(线程池在这里需要传入四个初始化参数)
65 | // (1):minThreadCount 线程池最小线程数目
66 | // (2):maxThreadCount 线程池最大线程数目
67 | // (3):maxWaitSeconds 当前线程等待maxWaitSeconds秒后仍然接收不到新来的任务就会自杀
68 | // (4):addOnceThreadCount 线程池可以扩增时,一次批量增加的线程数
69 | pool.initThreadPool(5, 15, 3, 5);
70 |
71 | // 3.将你需要执行的任务用createNewWork方法放入线程池中执行
72 | // 由于Java不支持函数式编程,这里要传入Runnable接口的子实现类,重写run方法作为你要执行的真正任务
73 | pool.createNewWork(new ActualWork(10));
74 | }
75 |
76 | @Slf4j
77 | @AllArgsConstructor
78 | class ActualWork implements Runnable {
79 | // 可以用成员变量支持参数传递(由于Runnable不支持参数传递)
80 | private Integer workId;
81 |
82 | @Override
83 | public void run() {
84 | // 1.工作时打印当前任务的ID号
85 | log.info("work {} run in the thread pool", workId);
86 | // 2.当前线程睡上5s(模拟当前线程处理该任务5s)
87 | try {
88 | Thread.sleep(5 * 1000);
89 | } catch (InterruptedException e) {
90 | log.error(e.getMessage());
91 | }
92 | // 3.当前任务结束
93 | log.info("work {} end", workId);
94 | }
95 | }
96 | ```
97 |
98 | ### 🌍 设计原则
99 |
100 | #### 🔒 锁设计
101 |
102 | > 只设置一把“大”的互斥锁,由于任务队列和线程池列表均使用了**非同步**的数据结构,**因此需要这把锁同时锁住线程池对象和任务队列对象**。涉及到添加新任务和线程内任务调度时均需要使用这把锁进行同步控制(保证同一时刻始终只有一个线程持有锁,不会出现死锁现象)。
103 | >
104 | > 使用时只涉及一把锁,这种设计有效地**防止了死锁现象**的发生,这把锁**只有在线程后台真正执行用户传入的任务对象时被释放**(任务执行完毕后重新与其它线程竞争锁恢复状态),保证了操作的原子性。由于线程要执行的任务时间长而这些其它原子性操作执行速度快,因此这种设计又不失线程池“后台”多线程执行任务的高效性。
105 |
106 | #### 🔨 调度原则(线程自身调度自身)
107 |
108 |
109 |
110 | > 每个线程均在一个 while(true) 循环中不断循环等待任务,调度主要分为以下情况
111 |
112 | - 当前线程在循环中一上来就发现任务队列中存在可执行的任务,那么就取出该任务执行
113 | - 当前线程一上来没有发现任务队列中存在可执行的任务,那么就**进入该锁的一个条件变量上进行超时等待**(**等待的同时释放对象锁**,其他线程也可以竞争锁判断)
114 | - 若当前线程等待未超时,**一定说明任务队列中出现了新的任务**(**创建任务时,会往条件变量里发 signal 信号随机唤醒其中的一个正在超时等待的线程工作**),此时这个被唤醒的线程重新进入循环抢锁执行新任务,如果有机会抢到锁就可以执行新任务,没有抢过其他线程就继续等待
115 | - 若当前线程等待超过预设的超时等待时间,这时候当前线程要**检查是否可以自杀**,如果当前线程满足自杀条件,那么当前线程自杀从而退出任务循环,线程池线程数量缩减;若不可以自杀则继续进入任务循环
116 |
117 | #### 🎨 线程池线程数量动态伸缩原则
118 |
119 | > 动态增加线程数目:在创建任务时检查当前线程池是否存在空闲的线程,**若创建新任务时不存在空闲线程且当前线程池中线程总数小于预设的 maxThreadCount 最大线程数量**,说明可以动态扩充,**一次批量增加 addOnceThreadCount 个数目**的线程。
120 | >
121 | > 动态缩减线程数目:每个线程在自身的任务循环中若**等待超时(在条件变量中等待 maxWaitSeconds 秒后仍然没有收到条件变量发来的新任务来临信号,这个线程继续等也没有意义)**,**若当前线程池线程数大于预设的线程池最小线程数 minThreadCount ,说明当前线程可以自杀,线程池动态缩减**,若当前线程池线程数已经是预设的线程池最小线程数,不可以自杀,继续循环接受任务。
122 |
123 | #### 📌 设计模式-策略模式
124 |
125 | > 使用 Runnable 接口聚合在线程池的任务对象中,使用时可以动态传入自身覆写的策略对象作为实际任务对象传入线程池,利用多态使任务队列不依赖于某个具体任务而依赖于一个抽象的通用接口,可拓展性强,耦合度低
126 |
127 | ### ✔ 线程池伸缩性测试输出
128 |
129 | #### 🔍 测试类代码
130 |
131 | ```Java
132 | package com.fyh.threadpool;
133 |
134 | import com.fyh.threadpool.main.StretchableThreadPool;
135 | import lombok.AllArgsConstructor;
136 | import lombok.Data;
137 | import lombok.extern.slf4j.Slf4j;
138 | import org.junit.jupiter.api.Test;
139 | import org.springframework.boot.test.context.SpringBootTest;
140 |
141 | @Slf4j
142 | @SpringBootTest
143 | class ThreadPoolApplicationTest {
144 |
145 | @Test
146 | public void testStretchablePool() throws InterruptedException {
147 | // 1.测试线程池,创建可伸缩线程池对象
148 | StretchableThreadPool pool = new StretchableThreadPool();
149 |
150 | // 2.初始化线程池,设置伸缩范围[5,15],等待3s自杀,一次批量增加5个线程
151 | pool.initThreadPool(5, 15, 3, 5);
152 |
153 | // 3.初始化后用createNewWork传入一个实现了Runnable接口的任务对象,自动执行run方法
154 | for (int i = 0; i < 30; i++) {
155 | pool.createNewWork(new ActualWork(i + 1));
156 | Thread.sleep(100);
157 | }
158 |
159 | // 4.等待15s测试彻底结束
160 | Thread.sleep(15 * 1000);
161 | log.info("all work finished");
162 | }
163 | }
164 |
165 | @Data
166 | @Slf4j
167 | @AllArgsConstructor
168 | class ActualWork implements Runnable {
169 | private Integer workId;
170 |
171 | @Override
172 | public void run() {
173 | // 1.工作时打印当前任务的ID号
174 | log.info("work {} run in the thread pool", workId);
175 |
176 | // 2.当前线程睡上5s(模拟当前线程处理该任务5s)
177 | try {
178 | Thread.sleep(5 * 1000);
179 | } catch (InterruptedException e) {
180 | log.error(e.getMessage());
181 | }
182 |
183 | // 3.当前任务结束
184 | log.info("work {} end", workId);
185 | }
186 | }
187 | ```
188 |
189 | #### 📄 测试输出日志解析
190 |
191 | ```Java
192 | ThreadId --- LogContent
193 |
194 | # ----------------------------------------------------------------- #
195 | # 初始化时线程池设置最小有五个线程可以使用,最大有15个线程可以使用
196 | # 任务队列中放30个任务,每个任务打印当前任务的ID号
197 | main --- [ thread pool created, now has 5 threads ]
198 | # ----------------------------------------------------------------- #
199 |
200 | # ----------------------------------------------------------------- #
201 | # 新任务来了,线程从等待状态被条件变量唤醒执行任务
202 | main --- [ new work added for function ActualWork(workId=1) ]
203 | 1 --- [ thread 1 called by cond obj ]
204 | 1 --- [ ActualWork(workId=1)} ]
205 | 1 --- [ work 1 run in the thread pool ]
206 | main --- [ new work added for function ActualWork(workId=2) ]
207 | 4 --- [ thread 4 called by cond obj ]
208 | 4 --- [ ActualWork(workId=2)} ]
209 | 4 --- [ work 2 run in the thread pool ]
210 | main --- [ new work added for function ActualWork(workId=3) ]
211 | 5 --- [ thread 5 called by cond obj ]
212 | 5 --- [ ActualWork(workId=3)} ]
213 | 5 --- [ work 3 run in the thread pool ]
214 | main --- [ new work added for function ActualWork(workId=4) ]
215 | 3 --- [ thread 3 called by cond obj ]
216 | 3 --- [ ActualWork(workId=4)} ]
217 | 3 --- [ work 4 run in the thread pool ]
218 | main --- [ new work added for function ActualWork(workId=5) ]
219 | 2 --- [ thread 2 called by cond obj ]
220 | 2 --- [ ActualWork(workId=5)} ]
221 | 2 --- [ work 5 run in the thread pool ]
222 | # ----------------------------------------------------------------- #
223 |
224 | # ----------------------------------------------------------------- #
225 | main --- [ new work added for function ActualWork(workId=6) ]
226 | main --- [ * thread pool extended. now has 10 threads ]
227 | # 已经有5个线程在打工了,此时池子里面没有空闲的线程了(但是任务还在来),线程数还没到达预设的最大值
228 | # 因此需要扩容,按照每次递增5个线程来进行线程数目的增加,此时有10个线程了,继续分配新来的任务执行
229 | # ----------------------------------------------------------------- #
230 |
231 | # ----------------------------------------------------------------- #
232 | 6 --- [ ActualWork(workId=6)} ]
233 | 6 --- [ work 6 run in the thread pool ]
234 | main --- [ new work added for function ActualWork(workId=7) ]
235 | 7 --- [ thread 7 called by cond obj ]
236 | 7 --- [ ActualWork(workId=7)} ]
237 | 7 --- [ work 7 run in the thread pool ]
238 | main --- [ new work added for function ActualWork(workId=8) ]
239 | 8 --- [ thread 8 called by cond obj ]
240 | 8 --- [ ActualWork(workId=8)} ]
241 | 8 --- [ work 8 run in the thread pool ]
242 | main --- [ new work added for function ActualWork(workId=9) ]
243 | 9 --- [ thread 9 called by cond obj ]
244 | 9 --- [ ActualWork(workId=9)} ]
245 | 9 --- [ work 9 run in the thread pool ]
246 | main --- [ new work added for function ActualWork(workId=10) ]
247 | 10 --- [ thread 10 called by cond obj ]
248 | 10 --- [ ActualWork(workId=10)} ]
249 | 10 --- [ work 10 run in the thread pool ]
250 | # ----------------------------------------------------------------- #
251 |
252 |
253 | # ----------------------------------------------------------------- #
254 | # 已经有10个线程在打工了,此时池子里面没有空闲的线程了(但是任务还在来),线程数还没到达预设的最大值
255 | # 因此需要扩容,按照每次递增5个线程来进行线程数目的增加,此时有15个线程了,继续分配新来的任务执行
256 | # 注意这里已经有15个,达到了预设的最大值了,线程池如果任务再来了就没办法继续扩充了
257 | main --- [ new work added for function ActualWork(workId=11) ]
258 | main --- [ * thread pool extended. now has 15 threads ]
259 | # ----------------------------------------------------------------- #
260 |
261 | # ----------------------------------------------------------------- #
262 | 11 --- [ ActualWork(workId=11)} ]
263 | 11 --- [ work 11 run in the thread pool ]
264 | main --- [ new work added for function ActualWork(workId=12) ]
265 | 12 --- [ thread 12 called by cond obj ]
266 | 12 --- [ ActualWork(workId=12)} ]
267 | 12 --- [ work 12 run in the thread pool ]
268 | main --- [ new work added for function ActualWork(workId=13) ]
269 | 13 --- [ thread 13 called by cond obj ]
270 | 13 --- [ ActualWork(workId=13)} ]
271 | 13 --- [ work 13 run in the thread pool ]
272 | main --- [ new work added for function ActualWork(workId=14) ]
273 | 14 --- [ thread 14 called by cond obj ]
274 | 14 --- [ ActualWork(workId=14)} ]
275 | 14 --- [ work 14 run in the thread pool ]
276 | main --- [ new work added for function ActualWork(workId=15) ]
277 | 15 --- [ thread 15 called by cond obj ]
278 | 15 --- [ ActualWork(workId=15)} ]
279 | 15 --- [ work 15 run in the thread pool ]
280 | # ----------------------------------------------------------------- #
281 |
282 | # ----------------------------------------------------------------- #
283 | # 新任务加入,无法扩充,线程都在忙,因此在队列中等待处理
284 | main --- [ new work added for function ActualWork(workId=16) ]
285 | main --- [ new work added for function ActualWork(workId=17) ]
286 | main --- [ new work added for function ActualWork(workId=18) ]
287 | main --- [ new work added for function ActualWork(workId=19) ]
288 | main --- [ new work added for function ActualWork(workId=20) ]
289 | main --- [ new work added for function ActualWork(workId=21) ]
290 | main --- [ new work added for function ActualWork(workId=22) ]
291 | main --- [ new work added for function ActualWork(workId=23) ]
292 | main --- [ new work added for function ActualWork(workId=24) ]
293 | main --- [ new work added for function ActualWork(workId=25) ]
294 | main --- [ new work added for function ActualWork(workId=26) ]
295 | main --- [ new work added for function ActualWork(workId=27) ]
296 | main --- [ new work added for function ActualWork(workId=28) ]
297 | main --- [ new work added for function ActualWork(workId=29) ]
298 | main --- [ new work added for function ActualWork(workId=30) ]
299 | # ----------------------------------------------------------------- #
300 |
301 | # ----------------------------------------------------------------- #
302 | # 线程处理完旧任务,可以继续处理新任务
303 | 1 --- [ work 1 end ]
304 | 1 --- [ ActualWork(workId=16)} ]
305 | 1 --- [ work 16 run in the thread pool ]
306 | 4 --- [ work 2 end ]
307 | 4 --- [ ActualWork(workId=17)} ]
308 | 4 --- [ work 17 run in the thread pool ]
309 | 5 --- [ work 3 end ]
310 | 5 --- [ ActualWork(workId=18)} ]
311 | 5 --- [ work 18 run in the thread pool ]
312 | 3 --- [ work 4 end ]
313 | 3 --- [ ActualWork(workId=19)} ]
314 | 3 --- [ work 19 run in the thread pool ]
315 | 2 --- [ work 5 end ]
316 | 2 --- [ ActualWork(workId=20)} ]
317 | 2 --- [ work 20 run in the thread pool ]
318 | 6 --- [ work 6 end ]
319 | 6 --- [ ActualWork(workId=21)} ]
320 | 6 --- [ work 21 run in the thread pool ]
321 | 7 --- [ work 7 end ]
322 | 7 --- [ ActualWork(workId=22)} ]
323 | 7 --- [ work 22 run in the thread pool ]
324 | 8 --- [ work 8 end ]
325 | 8 --- [ ActualWork(workId=23)} ]
326 | 8 --- [ work 23 run in the thread pool ]
327 | 9 --- [ work 9 end ]
328 | 9 --- [ ActualWork(workId=24)} ]
329 | 9 --- [ work 24 run in the thread pool ]
330 | 10 --- [ work 10 end ]
331 | 10 --- [ ActualWork(workId=25)} ]
332 | 10 --- [ work 25 run in the thread pool ]
333 | 11 --- [ work 11 end ]
334 | 11 --- [ ActualWork(workId=26)} ]
335 | 11 --- [ work 26 run in the thread pool ]
336 | 12 --- [ work 12 end ]
337 | 12 --- [ ActualWork(workId=27)} ]
338 | 12 --- [ work 27 run in the thread pool ]
339 | 13 --- [ work 13 end ]
340 | 13 --- [ ActualWork(workId=28)} ]
341 | 13 --- [ work 28 run in the thread pool ]
342 | 14 --- [ work 14 end ]
343 | 14 --- [ ActualWork(workId=29)} ]
344 | 14 --- [ work 29 run in the thread pool ]
345 | 15 --- [ work 15 end ]
346 | 15 --- [ ActualWork(workId=30)} ]
347 | 15 --- [ work 30 run in the thread pool ]
348 | 1 --- [ work 16 end ]
349 | 4 --- [ work 17 end ]
350 | 5 --- [ work 18 end ]
351 | 3 --- [ work 19 end ]
352 | 2 --- [ work 20 end ]
353 | 6 --- [ work 21 end ]
354 | 7 --- [ work 22 end ]
355 | 8 --- [ work 23 end ]
356 | 9 --- [ work 24 end ]
357 | 10 --- [ work 25 end ]
358 | 11 --- [ work 26 end ]
359 | 12 --- [ work 27 end ]
360 | 13 --- [ work 28 end ]
361 | 14 --- [ work 29 end ]
362 | 15 --- [ work 30 end ]
363 | # 分配的30个任务此时都被这些线程做完了
364 | # ----------------------------------------------------------------- #
365 |
366 | # ----------------------------------------------------------------- #
367 | # 原来的15个线程一直在等,等半天也等不到新任务了(因为预设了30个任务)
368 | # 等不到就开始自动销毁,一个一个自杀,直到预设的最小线程数量5就停止自杀
369 | 1 --- [ * thread 1 end, left 14 threads in pool ]
370 | 4 --- [ * thread 4 end, left 13 threads in pool ]
371 | 5 --- [ * thread 5 end, left 12 threads in pool ]
372 | 3 --- [ * thread 3 end, left 11 threads in pool ]
373 | 2 --- [ * thread 2 end, left 10 threads in pool ]
374 | 6 --- [ * thread 6 end, left 9 threads in pool ]
375 | 7 --- [ * thread 7 end, left 8 threads in pool ]
376 | 8 --- [ * thread 8 end, left 7 threads in pool ]
377 | 9 --- [ * thread 9 end, left 6 threads in pool ]
378 | 10 --- [ * thread 10 end, left 5 threads in pool ]
379 | # ----------------------------------------------------------------- #
380 | main --- [ all work finished ]
381 | ```
382 |
383 |
--------------------------------------------------------------------------------
/img/task-arrangement.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------