# 自定義一個簡單的線程池
## 寫在前面
在學習這個的時候,我一直在想,怎樣理清思路,把中間的點一個一個串起來,然后自己默寫出來,所以這個筆記我改了很多次,之前整理了很多理論知識,比如為什么使用線程池?線程池的優點等等,后來都刪掉了。理論知識google一下,百度一下都寫得非常好,所以我這里也沒必要copy一份粘貼在這里,這里就以理清思路為主。
### 搭建一個最簡單的框架
這里先把最基本的代碼寫出來,我們先定義一個SimpleThreadPool類。
成員屬性兩個:
**size**: *線程池里線程的數量*
**DEFAULT_SIZE**: *默認線程池的線程數量*
成員方法:
兩個構造方法,這里只是構造SimpleThreadPool這個類的size。
然后構造完成后調用init(),我們再編寫一個init方法。
定義一個枚舉代表我們工作線程的四個狀態:
**FREE**:*可以使用狀態*
**RUNNING**:*正在運行狀態*
**BLOCKED**:*阻塞狀態*
**DEAD**:*結束狀態*
定義一個WorkerTask代表我們的工作線程,繼承Thead并重寫run方法,其中的狀態的構造等代碼暫時自行發揮即可。
```java
public class SimpleThreadPool {
private final int size;
private final static int DEFAULT_SIZE = 10;
public SimpleThreadPool() {
this(DEFAULT_SIZE);
}
public SimpleThreadPool(int size) {
this.size = size;
init();
}
private void init() {
}
/**
* 線程task狀態
*/
private enum TaskState {
FREE, RUNNING, BLOCKED, DEAD
}
/**
* workTask
*/
private static class WorkerTask extends Thread {
private volatile TaskState taskState = TaskState.FREE; //默認free狀態
/**
* 獲取workTask的taskState狀態
* @return TaskState
*/
public TaskState getTaskState() {
return this.taskState;
}
@Override
public void run() {
//TODO
}
/**
* 關閉線程重置TaskState為DEAD
*/
public void close() {
this.taskState = TaskState.DEAD;
}
}
}
```
### 完善工作線程邏輯
接下來開始一步一步完善程序:
1. WorkerTask中引入ThreadGroup
```java
public WorkerTask(ThreadGroup threadGroup,String name){
super(threadGroup,name);
}
```
2. 接下來完善run方法,因為run方法不能執行完程序就掛掉,如果執行完就掛掉了也就沒意義了,所以這里使用while:
```java
@Override
public void run() {
while(this.taskState != TaskState.DEAD){
//TODO 執行任務
}
}
```
3. 接下來就要考慮我們執行的任務在哪里獲取呢?這里就要用到任務隊列了:
```java
private final static LinkedList<Runnable> TASK_QUEUE = new LinkedList<>();
```
完善我們的while循環:
```java
@Override
public void run() {
while (this.taskState != TaskState.DEAD) {
synchronized (TASK_QUEUE) {
// 任務隊列為空,則進入阻塞狀態
while (TASK_QUEUE.isEmpty()) {
try {
this.taskState = TaskState.BLOCKED;
TASK_QUEUE.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
```
注意看這個位置:

如果我們在任務隊列為空的情況下,打斷wait()的線程是退出到while循環里的,所以我們要加一個lable:

4. 接下來我們編寫**任務隊列不為空**情況下的代碼:
```java
@Override
public void run() {
OUTER:
while (this.taskState != TaskState.DEAD) {
synchronized (TASK_QUEUE) {
Runnable runnable;
// 任務隊列為空,則進入阻塞狀態
while (TASK_QUEUE.isEmpty()) {
try {
this.taskState = TaskState.BLOCKED;
TASK_QUEUE.wait();
} catch (InterruptedException e) {
System.out.println("Closed.");
break OUTER;
}
}
// 任務隊列不為空,取出任務
runnable = TASK_QUEUE.removeFirst();
// 任務不為空,則執行任務
if (runnable != null) {
this.taskState = TaskState.RUNNING;
runnable.run();
this.taskState = TaskState.FREE;
}
}
}
}
```
5.這樣工作線程就定義完了,接下來就要完善提交任務了,在提交任務之前,我們首先要構建。
我們在SimpleThreadPool類里面增加creatWorkeTask方法:
```java
private void creatWorkeTask(String name) {
}
```
這里我們打算用creatWorkeTask調用WorkerTask,WorkerTask的兩個參數,一個線程組,一個名字,這個名字我們來通過前綴+自增的方式生成,所以:
(注意這里是volatile)
```java
private static volatile int seg=0;
```
增加名字的前綴
```JAVA
/**
* 線程名前綴
*/
private final static String THREAD_PREFIX = "SIMPLE_THREAD_POOL-";
```
增加一個ThreadGroup:
```java
/**
* 線程組
*/
private final static ThreadGroup GROUP = new ThreadGroup("Pool_Group");
```
這樣就開始完善我們的creatWorkeTask方法了:
```java
private void creatWorkeTask(String name) {
WorkerTask task = new WorkerTask(GROUP,THREAD_PREFIX+(seg++));
task.start();
THREAD_QUEUE.add(task);
}
```
這里start后我們把他放在線程隊列里,我們定義一個List來存放WorkerTask,以便于我們管理。
```java
/**
* 線程隊列
*/
private static final List<WorkerTask> THREAD_QUEUE = new ArrayList<>();
```
6.提交任務我們就寫到這里,現在開始編寫init方法:
```java
private void init() {
for (int i = 0; i < size; i++) {
creatWorkeTask();
}
}
```
7.這時候我們如果調用SimpleThreadPool,他去init的時候,其實WorkerTask是wait狀態,因為TASK_QUEUE是空的,這時候我們就需要一個對外開放的接口來操作TASK_QUEUE了:
(這里有個細節就是因為TASK_QUEUE在我們的工作隊列里是有讀操作的,所以我們這里要加鎖才行)
```java
public void submit(Runnable runnable){
synchronized (TASK_QUEUE){
TASK_QUEUE.addLast(runnable);
TASK_QUEUE.notifyAll();
}
}
```
8.接下來就是激動人心的時刻,我們簡單調用一下看看效果:
```java
public static void main(String[] args) {
SimpleThreadPool threadPool = new SimpleThreadPool();
IntStream.rangeClosed(0, 40)
.forEach(i -> {
threadPool.submit(() -> {
System.out.println("The Runnable" + i + " be serviced by " + Thread.currentThread().getName()+" start");
try {
Thread.sleep(1_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("The Runnable" + i + " be serviced by " + Thread.currentThread().getName()+" finished");
});
});
}
```
9.補充一點,注意看這里的代碼:

調整后的代碼如下:

看一下運行效果:

一共10個線程,執行了40個任務,后面我們再繼續完善線程池,增加拒絕策略,停止等功能。
- 微服務
- 服務器相關
- 操作系統
- 極客時間操作系統實戰筆記
- 01 程序的運行過程:從代碼到機器運行
- 02 幾行匯編幾行C:實現一個最簡單的內核
- 03 黑盒之中有什么:內核結構與設計
- Rust
- 入門:Rust開發一個簡單的web服務器
- Rust的引用和租借
- 函數與函數指針
- Rust中如何面向對象編程
- 構建單線程web服務器
- 在服務器中增加線程池提高吞吐
- Java
- 并發編程
- 并發基礎
- 1.創建并啟動線程
- 2.java線程生命周期以及start源碼剖析
- 3.采用多線程模擬銀行排隊叫號
- 4.Runnable接口存在的必要性
- 5.策略模式在Thread和Runnable中的應用分析
- 6.Daemon線程的創建以及使用場景分析
- 7.線程ID,優先級
- 8.Thread的join方法
- 9.Thread中斷Interrupt方法學習&采用優雅的方式結束線程生命周期
- 10.編寫ThreadService實現暴力結束線程
- 11.線程同步問題以及synchronized的引入
- 12.同步代碼塊以及同步方法之間的區別和關系
- 13.通過實驗分析This鎖和Class鎖的存在
- 14.多線程死鎖分析以及案例介紹
- 15.線程間通信快速入門,使用wait和notify進行線程間的數據通信
- 16.多Product多Consumer之間的通訊導致出現程序假死的原因分析
- 17.使用notifyAll完善多線程下的生產者消費者模型
- 18.wait和sleep的本質區別
- 19.完善數據采集程序
- 20.如何實現一個自己的顯式鎖Lock
- 21.addShutdownHook給你的程序注入鉤子
- 22.如何捕獲線程運行期間的異常
- 23.ThreadGroup API介紹
- 24.線程池原理與自定義線程池一
- 25.給線程池增加拒絕策略以及停止方法
- 26.給線程池增加自動擴充,閑時自動回收線程的功能
- JVM
- C&C++
- GDB調試工具筆記
- C&C++基礎
- 一個例子理解C語言數據類型的本質
- 字節順序-大小端模式
- Php
- Php源碼閱讀筆記
- Swoole相關
- Swoole基礎
- php的五種運行模式
- FPM模式的生命周期
- OSI網絡七層圖片速查
- IP/TCP/UPD/HTTP
- swoole源代碼編譯安裝
- 安全相關
- MySql
- Mysql基礎
- 1.事務與鎖
- 2.事務隔離級別與IO的關系
- 3.mysql鎖機制與結構
- 4.mysql結構與sql執行
- 5.mysql物理文件
- 6.mysql性能問題
- Docker&K8s
- Docker安裝java8
- Redis
- 分布式部署相關
- Redis的主從復制
- Redis的哨兵
- redis-Cluster分區方案&應用場景
- redis-Cluster哈希虛擬槽&簡單搭建
- redis-Cluster redis-trib.rb 搭建&原理
- redis-Cluster集群的伸縮調優
- 源碼閱讀筆記
- Mq
- ELK
- ElasticSearch
- Logstash
- Kibana
- 一些好玩的東西
- 一次折騰了幾天的大華攝像頭調試經歷
- 搬磚實用代碼
- python讀取excel拼接sql
- mysql大批量插入數據四種方法
- composer好用的鏡像源
- ab
- 環境搭建與配置
- face_recognition本地調試筆記
- 虛擬機配置靜態ip
- Centos7 Init Shell
- 發布自己的Composer包
- git推送一直失敗怎么辦
- Beyond Compare過期解決辦法
- 我的Navicat for Mysql
- 小錯誤解決辦法
- CLoin報錯CreateProcess error=216
- mysql error You must reset your password using ALTER USER statement before executing this statement.
- VM無法連接到虛擬機
- Jetbrains相關
- IntelliJ IDEA 筆記
- CLoin的配置與使用
- PhpStormDocker環境下配置Xdebug
- PhpStorm advanced metadata
- PhpStorm PHP_CodeSniffer