本文主要介紹線程池的概念,原理,以及簡單實現一個線程池,若文中有不足或錯誤之處,請指出(ps:感激涕零,不要讓我陷入錯誤的誤區。。。)
一:線程池的基本概念和原理
在此之前,先來思考一個問題,爲啥要用線程池呢?
線程越多,不一定就會執行的越快,受到CPU的影響,我們要控制線程的數量,線程池它的一個作用,就是用來管理線程的數量的。一般在計算機中,線程的數量是CPU數量的1到2倍。
線程池主要有下面幾部分組成:
1:線程池管理器。用來管理線程池的,主要可以創建線程池,銷燬線程池等。
2:工作線程。執行任務的線程,可以循環的去執行不同的任務。
3:任務接口。必須要去實現的接口,主要用來線程的調度,任務的收尾以及任務的狀態等。
4:工作隊列。存放任務的隊列。
實現一個線程池,首先需要知道線程池的基本原理,我們知道線程池有個頂級接口Excutor,初始化一個線程池,有以下幾個參數需要了解:
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
5, //核心線程數
10, //最大線程數
5, //若最大線程數5s內沒有任務執行,就銷燬
TimeUnit.SECONDS, //時間單位
new LinkedBlockingDeque<>() //隊列
);
1:核心線程數,線程池的核心線程,當有任務提交過來的時候就創建一個線程,執行任務。
2:工作隊列,當核心線程數量達到我們設置的值時,就去判斷工作隊列是不是已經滿了,要是沒滿,就把任務放入工作隊列。
3:最大線程數,當工作隊列已滿,這時候就看是否達到了最大線程數,要是沒有,就建立線程執行任務,要是達到了最大線程數,就執行拒絕策略。
二:實現一個簡單的線程池
在實現之前,需要思考:實現線程池,需要去實現什麼?怎麼實現?需要做什麼事情?
根據上面的介紹知道,實現一個線程池,肯定需要一個隊列去存放任務,還需要一個集合去存放線程:
/** 需要一個任務隊列,用來存放任務 */
private BlockingDeque<Runnable> blockingDeque;
/** 需要一個集合,存放工作線程 */
private static List<Thread> workerList;
那麼接下來還需要做什麼呢?既然任務隊列和線程都有了,接下來就是從任務隊列拿任務,然後線程循環的去執行任務:
/** 工作線程執行任務,需要可以循環的執行 */
public class Worker extends Thread {
private ThreadPoolDemo threadPool;
//構造方法
public Worker(ThreadPoolDemo pool) {
this.threadPool = pool;
}
@Override
public void run() {
//判斷條件,線程是否關閉,任務隊列中是否還有任務
while (isWorker == false || blockingDeque.size() > 0) {
Runnable task = null;
try {
if (isWorker) {
//非阻塞
task = blockingDeque.poll();
} else {
//阻塞
task = blockingDeque.take();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
if (task != null) {
task.run();
}
}
}
}
下面就是初始化線程池的方法了,指定線程的數量,隊列的大小等:
/** 初始化一個線程池
* 傳入參數:線程數,工作隊列數
*/
public ThreadPoolDemo(int threadSize, int queueSize) {
//初始化隊列
blockingDeque = new LinkedBlockingDeque<>(queueSize);
//初始化工作線程集合(線程安全的)
workerList = Collections.synchronizedList(new ArrayList<>());
for (int i = 0; i < threadSize; i ++) {
Worker worker = new Worker(this);
//啓動線程
worker.start();
//把線程放入線程集合
workerList.add(worker);
}
}
然後,就是提供一個對外的接口,用來提交任務,下面提供一個阻塞和一個非阻塞的提交任務接口:
/** 提交任務的接口,阻塞方式 */
public void submit(Runnable task) {
try {
//關閉線程池,就不會有任務提交
if (!this.isWorker) {
this.blockingDeque.put(task);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/** 提交任務的接口,非阻塞方式 */
public boolean execute(Runnable task) {
//關閉線程池,就不會有任務提交
if (this.isWorker) {
return false;
} else {
return this.blockingDeque.offer(task);
}
}
最後,就是關閉線程池,關閉線程池需要滿足幾個條件:
1:當線程池關閉的時候,不會有新的任務提交過來了;
2:線程再去拿任務的時候,就不能再阻塞了;
3:已經阻塞的線程,需要打斷阻塞。
/** 標誌位,代表線程池是否關閉 */
public volatile boolean isWorker = false;
/**
* 線程池關閉時:
* 1:不會有新的任務提交
* 2:隊列中的任務提交時,不會再阻塞
* 3:已經阻塞的線程,需要打斷阻塞
* 4:需要隊列中的任務提交完畢
*/
public void shutDown() {
this.isWorker = true;
for (Thread worker : workerList) {
//線程阻塞的話,就要打斷阻塞
if (worker.getState().equals(Thread.State.BLOCKED) ||
worker.getState().equals(Thread.State.WAITING)) {
worker.interrupt();
}
}
}
好了,到此基本的線程池就實現了。下面寫一個測試:
public static void main(String[] args) {
ThreadPoolDemo pool = new ThreadPoolDemo(3, 6);
for (int i = 0; i<6; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("任務正在被提交");
}
});
pool.submit(thread);
}
}
最後的最後,知識有限,可能說的不夠全面和深入,見諒。
--- 我自是年少,韶華傾付