看過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自身的執行緒池簡單的多,我們去掉了各種複雜的處理方式,只保留了最核心的原理:執行緒池的使用者為任務佇列中新增任務,而執行緒池本身則從任務佇列中消費任務並執行任務。
只要理解了這個核心原理,接下來的程式碼就簡單多了。在實作這個簡單的執行緒池時,我們可以將整個實作過程進行拆解。拆解後的實作流程為:定義核心欄位、建立內部類別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>();
在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的构造方法中传入线程池的大小和阻塞队列 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--->>你好執行緒池
Thread-9--->>>你好執行緒池
Thread-5--->>>你好執行緒池
Thread-8--->>>你好線程池
Thread-4--->>你好執行緒池
Thread-1--->>你好執行緒池
Thread-2--->>你好執行緒池
Thread-5--->>>你好執行緒池
Thread-9--->>>你好線程池
Thread-0--->>>你好線程池
以上是Java如何使用執行緒池?代碼實現。的詳細內容。更多資訊請關注PHP中文網其他相關文章!