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