說明
手寫的一個簡單的線程池,旨在幫助瞭解線程池的工作原理。
核心內容
- 核心工作線程
- 任務阻塞隊列
定義一個內部類去實現核心工作線程
/**
* 內部類:工作的核心線程
*/
private final class WorkerThread extends Thread {
String name;
//構造方法
WorkerThread(String name) {
this.name = name;
}
@Override
public void run() {
while (!getPoolState()) {
try {
Runnable task = taskQueue.take();
if (task != null) {
getThreadLog("任務開始執行:" + task.toString());
task.run();
}
//釋放內存
task = null;
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("線程關閉");
}
}
使用一個阻塞隊列存放需要處理的任務列表
//存放任務執行結束的隊列
private volatile BlockingQueue<Runnable> taskQueue;
//記錄線程池中線程的個數
private final Set<WorkerThread> workerThreadSet = new HashSet<>();
完整測試代碼
package com.leo.demo.threadtest.mythreadpool;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
/**
* @ClassName: MyThreadPool
* @Description: 小的線程池模型
* 1、線程池的
* @Author: leo825
* @Date: 2020-05-11 23:08
* @Version: 1.0
*/
public class MyThreadPool {
//核心線程數
private int corePoolSize;
//最大線程數
private int maximumPoolSize;
//存放任務執行結束的隊列
private volatile BlockingQueue<Runnable> taskQueue;
//記錄線程池中線程的個數
private final Set<WorkerThread> workerThreadSet = new HashSet<>();
//當前存放當前線程數
private int workerCount = 0;
//線程池關閉狀態
private volatile boolean THREADPOOL_SHUTDOWN = false;
/**
* 線程池構造方法,先不考慮參數校驗問題
*
* @param corePoolSize
* @param maximumPoolSize
* @param taskQueue
*/
MyThreadPool(int corePoolSize, int maximumPoolSize, BlockingQueue<Runnable> taskQueue) {
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.taskQueue = taskQueue;
//構造線程池中的線程
for (int i = 0; i < corePoolSize; i++) {
WorkerThread workerThread = new WorkerThread("poolThread_" + i);
workerThread.start();
workerThreadSet.add(workerThread);
workerCount++;
}
}
/**
* 向線程池中提交任務執行結束
*
* @param task
*/
public void submit(Runnable task) {
if (taskQueue.offer(task)) {
getThreadLog("任務執行結束添加成功: " + task.toString());
} else {
getThreadLog("任務執行結束添加失敗: " + task.toString());
}
}
/**
* 關閉線程池
*/
public void shutdown() {
getThreadLog("關閉線程池");
THREADPOOL_SHUTDOWN = true;
}
/**
* 內部類:工作的核心線程
*/
private final class WorkerThread extends Thread {
String name;
//構造方法
WorkerThread(String name) {
this.name = name;
}
@Override
public void run() {
while (!THREADPOOL_SHUTDOWN) {
try {
Runnable task = taskQueue.take();
if (task != null) {
getThreadLog("任務開始執行:" + task.toString());
task.run();
}
//釋放內存
task = null;
} catch (Exception e) {
e.printStackTrace();
}
}
getThreadLog("線程關閉");
}
}
/**
* 獲取線程名和時間
*
* @return
*/
public static void getThreadLog(String logContent) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("[");
stringBuffer.append(Thread.currentThread().getName());
stringBuffer.append(" ");
stringBuffer.append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()));
stringBuffer.append("] ");
stringBuffer.append(logContent);
System.out.println(stringBuffer.toString());
}
/**
* 測試main方法
*
* @param args
*/
public static void main(String[] args) throws InterruptedException {
//定義一個阻塞隊列存放任務
BlockingQueue<Runnable> workQueue = new LinkedBlockingDeque<>(5);
//構造自己的線程池
MyThreadPool threadPool = new MyThreadPool(3, 5, workQueue);
//任務執行結束數量
int size = 15;
for (int i = 0; i < size; i++) {
threadPool.submit(new Runnable() {
@Override
public void run() {
try {
int times = ThreadLocalRandom.current().nextInt(2, 6);
TimeUnit.SECONDS.sleep(times);
getThreadLog("任務執行結束 " + this.toString() + ",執行時長:" + times + " 秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
//休息20秒
TimeUnit.SECONDS.sleep(20);
//關閉線程池,此處不管用的,待後續分析源碼
threadPool.shutdown();
}
}
測試結果如下:
[Thread-0 2020-06-09 11:32:12.698] 任務開始執行:com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@3fee733d
[main 2020-06-09 11:32:12.696] 任務執行結束添加成功: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@3fee733d
[main 2020-06-09 11:32:12.699] 任務執行結束添加成功: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@2a84aee7
[main 2020-06-09 11:32:12.699] 任務執行結束添加成功: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@a09ee92
[main 2020-06-09 11:32:12.699] 任務執行結束添加成功: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@30f39991
[main 2020-06-09 11:32:12.700] 任務執行結束添加成功: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@452b3a41
[main 2020-06-09 11:32:12.700] 任務執行結束添加成功: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@4a574795
[main 2020-06-09 11:32:12.700] 任務執行結束添加失敗: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@f6f4d33
[main 2020-06-09 11:32:12.701] 任務執行結束添加失敗: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@23fc625e
[main 2020-06-09 11:32:12.701] 任務執行結束添加失敗: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@3f99bd52
[main 2020-06-09 11:32:12.701] 任務執行結束添加失敗: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@4f023edb
[main 2020-06-09 11:32:12.702] 任務執行結束添加失敗: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@3a71f4dd
[main 2020-06-09 11:32:12.703] 任務執行結束添加失敗: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@7adf9f5f
[main 2020-06-09 11:32:12.703] 任務執行結束添加失敗: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@85ede7b
[main 2020-06-09 11:32:12.703] 任務執行結束添加失敗: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@5674cd4d
[main 2020-06-09 11:32:12.704] 任務執行結束添加失敗: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@63961c42
[Thread-1 2020-06-09 11:32:12.709] 任務開始執行:com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@2a84aee7
[Thread-2 2020-06-09 11:32:12.711] 任務開始執行:com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@a09ee92
[Thread-0 2020-06-09 11:32:14.712] 任務執行結束 com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@3fee733d,執行時長:2 秒
[Thread-0 2020-06-09 11:32:14.714] 任務開始執行:com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@30f39991
[Thread-1 2020-06-09 11:32:16.710] 任務執行結束 com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@2a84aee7,執行時長:4 秒
[Thread-1 2020-06-09 11:32:16.711] 任務開始執行:com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@452b3a41
[Thread-2 2020-06-09 11:32:17.712] 任務執行結束 com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@a09ee92,執行時長:5 秒
[Thread-2 2020-06-09 11:32:17.712] 任務開始執行:com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@4a574795
[Thread-0 2020-06-09 11:32:17.715] 任務執行結束 com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@30f39991,執行時長:3 秒
[Thread-1 2020-06-09 11:32:19.711] 任務執行結束 com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@452b3a41,執行時長:3 秒
[Thread-2 2020-06-09 11:32:21.713] 任務執行結束 com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@4a574795,執行時長:4 秒
[main 2020-06-09 11:32:32.704] 關閉線程池
問題和思考
以上就是一個很小的線程池的模型,這個模型還存在很多問題。
- 線程池的最大線程數是核心線程滿、阻塞隊列滿之後線程池擴展到最大線程數,線程池擴容是如何實現的?
- 線程空閒後是如何進行銷燬的?
- 線程池相關的監控指標,歷史最大線程數、當前線程數、累計執行任務數?
- 線程池是如何銷燬的?
- 如果taskQueue隊列滿了,如何管理容量?
- 如果taskQueue中沒有任務了,線程池活躍線程數如何看?