摘要:花10分鐘開發一個極簡版的Java執行緒池,讓小伙伴們更好的理解執行緒池的核心原理,
本文分享自華為云社區《放大招了,冰河帶你10分鐘手擼Java執行緒池,yyds,趕快收藏吧》,作者:冰 河,
Java執行緒池核心原理
看過Java執行緒池原始碼的小伙伴都知道,在Java執行緒池中最核心的類就是ThreadPoolExecutor,而在ThreadPoolExecutor類中最核心的構造方法就是帶有7個引數的構造方法,如下所示,
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
各引數的含義如下所示,
- corePoolSize:執行緒池中的常駐核心執行緒數,
- maximumPoolSize:執行緒池能夠容納同時執行的最大執行緒數,此值大于等于1,
- keepAliveTime:多余的空閑執行緒存活時間,當空間時間達到keepAliveTime值時,多余的執行緒會被銷毀直到只剩下corePoolSize個執行緒為止,
- unit:keepAliveTime的單位,
- workQueue:任務佇列,被提交但尚未被執行的任務,
- threadFactory:表示生成執行緒池中作業執行緒的執行緒工廠,用戶創建新執行緒,一般用默認即可,
- handler:拒絕策略,表示當執行緒佇列滿了并且作業執行緒大于等于執行緒池的最大顯示數(maxnumPoolSize)時,如何來拒絕請求執行的runnable的策略,
并且Java的執行緒池是通過 生產者-消費者模式 實作的,執行緒池的使用方是生產者,而執行緒池本身就是消費者,
Java執行緒池的核心作業流程如下圖所示,

手擼Java執行緒池
我們自己手動實作的執行緒池要比Java自身的執行緒池簡單的多,我們去掉了各種復雜的處理方式,只保留了最核心的原理:執行緒池的使用者向任務佇列中添加任務,而執行緒池本身從任務佇列中消費任務并執行任務,

只要理解了這個核心原理,接下來的代碼就簡單多了,在實作這個簡單的執行緒池時,我們可以將整個實作程序進行拆解,拆解后的實作流程為:定義核心欄位、創建內部類WorkThread、創建ThreadPool類的構造方法和創建執行任務的方法,

定義核心欄位
首先,我們創建一個名稱為ThreadPool的Java類,并在這個類中定義如下核心欄位,
- DEFAULT_WORKQUEUE_SIZE:靜態常量,表示默認的阻塞佇列大小,
- workQueue:模擬實際的執行緒池使用阻塞佇列來實作生產者-消費者模式,
- workThreads:模擬實際的執行緒池使用List集合保存執行緒池內部的作業執行緒,
核心代碼如下所示,
//默認阻塞佇列大小 private static final int DEFAULT_WORKQUEUE_SIZE = 5; //模擬實際的執行緒池使用阻塞佇列來實作生產者-消費者模式 private BlockingQueue<Runnable> workQueue; //模擬實際的執行緒池使用List集合保存執行緒池內部的作業執行緒 private List<WorkThread> workThreads = new ArrayList<WorkThread>();
創建內部類WordThread
在ThreadPool類中創建一個內部類WorkThread,模擬執行緒池中的作業執行緒,主要的作用就是消費workQueue中的任務,并執行任務,由于作業執行緒需要不斷從workQueue中獲取任務,所以,這里使用了while(true)回圈不斷嘗試消費佇列中的任務,
核心代碼如下所示,
//內部類WorkThread,模擬執行緒池中的作業執行緒 //主要的作用就是消費workQueue中的任務,并執行 //由于作業執行緒需要不斷從workQueue中獲取任務,使用了while(true)回圈不斷嘗試消費佇列中的任務 class WorkThread extends Thread{ @Override public void run() { //不斷回圈獲取佇列中的任務 while (true){ //當沒有任務時,會阻塞 try { Runnable workTask = workQueue.take(); workTask.run(); } catch (InterruptedException e) { e.printStackTrace(); } } } }
創建ThreadPool類的構造方法
這里,我們為ThreadPool類創建兩個構造方法,一個構造方法中傳入執行緒池的容量大小和阻塞佇列,另一個構造方法中只傳入執行緒池的容量大小,
核心代碼如下所示,
//在ThreadPool的構造方法中傳入執行緒池的大小和阻塞佇列 public ThreadPool(int poolSize, BlockingQueue<Runnable> workQueue){ this.workQueue = workQueue; //創建poolSize個作業執行緒并將其加入到workThreads集合中 IntStream.range(0, poolSize).forEach((i) -> { WorkThread workThread = new WorkThread(); workThread.start(); workThreads.add(workThread); }); } //在ThreadPool的構造方法中傳入執行緒池的大小 public ThreadPool(int poolSize){ this(poolSize, new LinkedBlockingQueue<>(DEFAULT_WORKQUEUE_SIZE)); }
創建執行任務的方法
在ThreadPool類中創建執行任務的方法execute(),execute()方法的實作比較簡單,就是將方法接收到的Runnable任務加入到workQueue佇列中,
核心代碼如下所示,
//通過執行緒池執行任務 public void execute(Runnable task){ try { workQueue.put(task); } catch (InterruptedException e) { e.printStackTrace(); } }
完整原始碼
這里,我們給出手動實作的ThreadPool執行緒池的完整源代碼,如下所示,
package io.binghe.thread.pool; import java.util.ArrayList; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.stream.IntStream; /** * @author binghe * @version 1.0.0 * @description 自定義執行緒池 */ public class ThreadPool { //默認阻塞佇列大小 private static final int DEFAULT_WORKQUEUE_SIZE = 5; //模擬實際的執行緒池使用阻塞佇列來實作生產者-消費者模式 private BlockingQueue<Runnable> workQueue; //模擬實際的執行緒池使用List集合保存執行緒池內部的作業執行緒 private List<WorkThread> workThreads = new ArrayList<WorkThread>(); //在ThreadPool的構造方法中傳入執行緒池的大小和阻塞佇列 public ThreadPool(int poolSize, BlockingQueue<Runnable> workQueue){ this.workQueue = workQueue; //創建poolSize個作業執行緒并將其加入到workThreads集合中 IntStream.range(0, poolSize).forEach((i) -> { WorkThread workThread = new WorkThread(); workThread.start(); workThreads.add(workThread); }); } //在ThreadPool的構造方法中傳入執行緒池的大小 public ThreadPool(int poolSize){ this(poolSize, new LinkedBlockingQueue<>(DEFAULT_WORKQUEUE_SIZE)); } //通過執行緒池執行任務 public void execute(Runnable task){ try { workQueue.put(task); } catch (InterruptedException e) { e.printStackTrace(); } } //內部類WorkThread,模擬執行緒池中的作業執行緒 //主要的作用就是消費workQueue中的任務,并執行 //由于作業執行緒需要不斷從workQueue中獲取任務,使用了while(true)回圈不斷嘗試消費佇列中的任務 class WorkThread extends Thread{ @Override public void run() { //不斷回圈獲取佇列中的任務 while (true){ //當沒有任務時,會阻塞 try { Runnable workTask = workQueue.take(); workTask.run(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
沒錯,我們僅僅用了幾十行Java代碼就實作了一個極簡版的Java執行緒池,沒錯,這個極簡版的Java執行緒池的代碼卻體現了Java執行緒池的核心原理,
接下來,我們測驗下這個極簡版的Java執行緒池,
撰寫測驗程式
測驗程式也比較簡單,就是通過在main()方法中呼叫ThreadPool類的構造方法,傳入執行緒池的大小,創建一個ThreadPool類的實體,然后回圈10次呼叫ThreadPool類的execute()方法,向執行緒池中提交的任務為:列印當前執行緒的名稱--->> Hello ThreadPool,
整體測驗代碼如下所示,
package io.binghe.thread.pool.test; import io.binghe.thread.pool.ThreadPool; import java.util.stream.IntStream; /** * @author binghe * @version 1.0.0 * @description 測驗自定義執行緒池 */ public class ThreadPoolTest { public static void main(String[] args){ ThreadPool threadPool = new ThreadPool(10); IntStream.range(0, 10).forEach((i) -> { threadPool.execute(() -> { System.out.println(Thread.currentThread().getName() + "--->> Hello ThreadPool"); }); }); } }
接下來,運行ThreadPoolTest類的main()方法,會輸出如下資訊,
Thread-0--->> Hello ThreadPool Thread-9--->> Hello ThreadPool Thread-5--->> Hello ThreadPool Thread-8--->> Hello ThreadPool Thread-4--->> Hello ThreadPool Thread-1--->> Hello ThreadPool Thread-2--->> Hello ThreadPool Thread-5--->> Hello ThreadPool Thread-9--->> Hello ThreadPool Thread-0--->> Hello ThreadPool
至此,我們自定義的Java執行緒池就開發完成了,
總結
執行緒池的核心原理其實并不復雜,只要我們耐心的分析,深入其原始碼理解執行緒池的核心本質,你就會發現執行緒池的設計原來是如此的優雅,希望通過這個手寫執行緒池的小例子,能夠讓你更好的理解執行緒池的核心原理,
點擊關注,第一時間了解華為云新鮮技術~
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/550527.html
標籤:其他
下一篇:【manim影片教程】--相機