Java并發(fā)包線程池ThreadPoolExecutor的實現(xiàn)
線程池主要解決兩個問題:一是當(dāng)執(zhí)行大量異步任務(wù)時線程池能夠提供較好的性能。在不使用線程池時,每當(dāng)需要執(zhí)行異步任務(wù)時直接new一個線程來運行,而線程的創(chuàng)建和銷毀都是需要開銷的。線程池里面的線程是可復(fù)用的,不需要每次執(zhí)行異步任務(wù)時都重新創(chuàng)建和銷毀線程。二是線程池提供了一種資源限制和管理手段,比如可以限制線程的個數(shù),動態(tài)新增線程等。每個ThreadPoolExecutor也保留了一些基本的統(tǒng)計數(shù)據(jù),比如當(dāng)前線程池完成的任務(wù)數(shù)目等。
我們首先來看一下類圖

Excecutor是一個工具類,里面提供了許多靜態(tài)方法,這些方法根據(jù)用戶選擇返回不同的線程池實例。ThreadPool繼承了AbstractExecutorService。
下面我們看一下源碼,
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); private static final int COUNT_BITS = Integer.SIZE - 3; private static final int CAPACITY ? = (1 << COUNT_BITS) - 1; // runState is stored in the high-order bits private static final int RUNNING ? ?= -1 << COUNT_BITS; private static final int SHUTDOWN ? = ?0 << COUNT_BITS; private static final int STOP ? ? ? = ?1 << COUNT_BITS; private static final int TIDYING ? ?= ?2 << COUNT_BITS; private static final int TERMINATED = ?3 << COUNT_BITS;
成員變量ctl是一個Integer的原子變量,用來記錄當(dāng)前線程池狀態(tài)和線程池中的線程個數(shù),有點類似于ReentrantReadWriteLock使用一個變量來保存兩種信息。
線程池一共有五種狀態(tài):
- RUNNING:能接受新任務(wù),并且處理阻塞隊列里面的任務(wù)
- SHUTDOWN:拒絕接受新任務(wù)但是處理阻塞隊列里的任務(wù)
- STOP:拒絕新任務(wù)并且拋棄阻塞隊列里的任務(wù)
- TIDYING:所有任務(wù)都執(zhí)行完(包含阻塞隊列里面的任務(wù))后當(dāng)前線程池活動線程數(shù)為0,將要調(diào)用terminated方法。
- TERMINATED:終止?fàn)顟B(tài)。terminated方法調(diào)用完成以后的狀態(tài)。

線程池狀態(tài)轉(zhuǎn)換如下:
- RUNNING->SHUTDOWN:顯示調(diào)用shutdown方法,或者隱式調(diào)用finalize()方法里的shutdown()方法。
- RUNNINGSHUTDOWN->STOP:顯示調(diào)用shutdown方法
- SHUTDOWN->TIDYING:當(dāng)線程池和任務(wù)隊列都為空時
- STOP->TIDYING:當(dāng)線程池為空時
- TIDYING->TERMINATED:Terminated() hook方法執(zhí)行完畢時
線程池的使用
合理利用線程池能夠帶來三個好處:
- 降低資源消耗。減少了創(chuàng)建和銷毀線程的次數(shù),每個工作線程都可以被重復(fù)利用,可執(zhí)行多個任務(wù)。
- 提高響應(yīng)速度。當(dāng)任務(wù)到達(dá)時,任務(wù)可以不需要的等到線程創(chuàng)建就能立即執(zhí)行。
- 提高線程的可管理性。可以根據(jù)系統(tǒng)的承受能力,調(diào)整線程池中工作線線程的數(shù)目,防止因為消耗過多的內(nèi)存,而把服務(wù)器累趴下(每個線程需要大約1MB內(nèi)存,線程開的越多,消耗的內(nèi)存也就越大,最后死機(jī))。
在java.util.concurrent.Executors線程工廠類里面提供了一些靜態(tài)工廠,生成一些常用的線程池。官方建議使用Executors工程類來創(chuàng)建線程池對象。
Executors類中有個創(chuàng)建線程池的方法如下:
- public static ExecutorService newFixedThreadPool(int nThreads):返回線程池對象。(創(chuàng)建的是有界線程池,也就是池中的線程個數(shù)可以指定最大數(shù)量)
獲取到了一個線程池ExecutorService 對象,那么怎么使用呢,在這里定義了一個使用線程池對象的方法如下:
- public Future<?> submit(Runnable task):獲取線程池中的某一個線程對象,并執(zhí)行
Future接口:用來記錄線程任務(wù)執(zhí)行完畢后產(chǎn)生的結(jié)果。
使用線程池中線程對象的步驟:
- 創(chuàng)建線程池對象。
- 創(chuàng)建Runnable接口子類對象。(task)
- 提交Runnable接口子類對象。(take task)
- 關(guān)閉線程池(一般不做)。
Runnable實現(xiàn)類代碼:
public class MyRunnable implements Runnable {
? ? @Override
? ? public void run() {
? ? ? ? System.out.println("我要一個教練");
? ? ? ? try {
? ? ? ? ? ? Thread.sleep(2000);
? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? System.out.println("教練來了: " + Thread.currentThread().getName());
? ? ? ? System.out.println("教我游泳,交完后,教練回到了游泳池");
? ? }
}
public class ThreadPoolDemo {
? ? public static void main(String[] args) {
? ? ? ? // 創(chuàng)建線程池對象
? ? ? ? ExecutorService service = Executors.newFixedThreadPool(2);//包含2個線程對象
? ? ? ? // 創(chuàng)建Runnable實例對象
? ? ? ? MyRunnable r = new MyRunnable();
? ? ? ? //自己創(chuàng)建線程對象的方式
? ? ? ? // Thread t = new Thread(r);
? ? ? ? // t.start(); ---> 調(diào)用MyRunnable中的run()
? ? ? ? // 從線程池中獲取線程對象,然后調(diào)用MyRunnable中的run()
? ? ? ? service.submit(r);
? ? ? ? // 再獲取個線程對象,調(diào)用MyRunnable中的run()
? ? ? ? service.submit(r);
? ? ? ? service.submit(r);
? ? ? ? // 注意:submit方法調(diào)用結(jié)束后,程序并不終止,是因為線程池控制了線程的關(guān)閉。
? ? ? ? // 將使用完的線程又歸還到了線程池中
? ? ? ? // 關(guān)閉線程池
? ? ? ? //service.shutdown();
? ? }
}Callable測試代碼:
<T> Future<T> submit(Callable<T> task) : 獲取線程池中的某一個線程對象,并執(zhí)行.
Future : 表示計算的結(jié)果.
V get() : 獲取計算完成的結(jié)果。
public class ThreadPoolDemo2 {
? ? public static void main(String[] args) throws Exception {
? ? ? ? // 創(chuàng)建線程池對象
? ? ? ExecutorService service = Executors.newFixedThreadPool(2);//包含2個線程對象
? ? ? ? // 創(chuàng)建Runnable實例對象
? ? ? ? Callable<Double> c = new Callable<Double>() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public Double call() throws Exception {
? ? ? ? ? ? ? ? return Math.random();
? ? ? ? ? ? }
? ? ? ? };
? ? ? ? // 從線程池中獲取線程對象,然后調(diào)用Callable中的call()
? ? ? ? Future<Double> f1 = service.submit(c);
? ? ? ? // Futur 調(diào)用get() 獲取運算結(jié)果
? ? ? ? System.out.println(f1.get());
? ? ? ? Future<Double> f2 = service.submit(c);
? ? ? ? System.out.println(f2.get());
? ? ? ? Future<Double> f3 = service.submit(c);
? ? ? ? System.out.println(f3.get());
? ? }
}線程池的練習(xí)
public class Demo04 {
? ? public static void main(String[] args) throws ExecutionException, InterruptedException {
? ? ? ? ExecutorService pool = Executors.newFixedThreadPool(3);
? ? ? ? SumCallable sc = new SumCallable(100);
? ? ? ? Future<Integer> fu = pool.submit(sc);
? ? ? ? Integer integer = fu.get();
? ? ? ? System.out.println("結(jié)果: " + integer);
? ? ? ??
? ? ? ? SumCallable sc2 = new SumCallable(200);
? ? ? ? Future<Integer> fu2 = pool.submit(sc2);
? ? ? ? Integer integer2 = fu2.get();
? ? ? ? System.out.println("結(jié)果: " + integer2);
? ? ? ? pool.shutdown();
? ? }
}public class SumCallable implements Callable<Integer> {
? ? private int n;
? ? public SumCallable(int n) {
? ? ? ? this.n = n;
? ? }
? ? @Override
? ? public Integer call() throws Exception {
? ? ? ? // 求1-n的和?
? ? ? ? int sum = 0;
? ? ? ? for (int i = 1; i <= n; i++) {
? ? ? ? ? ? sum += i;
? ? ? ? }
? ? ? ? return sum;
? ? }
}到此這篇關(guān)于Java并發(fā)包線程池ThreadPoolExecutor的實現(xiàn)的文章就介紹到這了,更多相關(guān)Java并發(fā)包線程池ThreadPoolExecutor內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java編程實現(xiàn)基于TCP協(xié)議的Socket聊天室示例
這篇文章主要介紹了Java編程實現(xiàn)基于TCP協(xié)議的Socket聊天室,結(jié)合實例形式詳細(xì)分析了java基于TCP協(xié)議的Socket聊天室客戶端與服務(wù)器端相關(guān)實現(xiàn)與使用技巧,需要的朋友可以參考下2018-01-01
IntelliJ IDEA中查看文件內(nèi)所有已聲明的方法(類似eclipse的outline)
今天小編就為大家分享一篇關(guān)于IntelliJ IDEA中查看文件內(nèi)所有已聲明的方法(類似eclipse的outline),小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-10-10
SpringBoot Admin2.0 集成Arthas的實現(xiàn)步驟
這篇文章主要介紹了SpringBoot Admin2.0 集成Arthas的實現(xiàn)步驟,幫助大家更好的理解和學(xué)習(xí)使用SpringBoot框架,感興趣的朋友可以了解下2021-04-04
IDEA創(chuàng)建springboot + mybatis項目全過程(步驟詳解)
這篇文章主要介紹了IDEA創(chuàng)建springboot + mybatis項目全過程及步驟詳解,本文通圖文實例代碼相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07
SpringBoot詳細(xì)講解如何創(chuàng)建及刷新Spring容器bean
前面看spring源碼時可以發(fā)現(xiàn)refresh()方法十分重要。在這個方法中會加載beanDefinition,同時創(chuàng)建bean對象。那么在springboot中有沒有使用這個refresh()方法呢2022-06-06

