Java線程池由淺入深掌握到精通
1.為什么使用線程池?
反復(fù)創(chuàng)建線程開銷大,可以復(fù)用線程池
過多的線程會占用太多的內(nèi)存
解決以上問題的方法:
- 用少量的線程,避免內(nèi)存占用過多
- 讓這部分線程都保持工作,且反復(fù)執(zhí)行任務(wù),避免生命周期的損耗
2.線程池的好處:
加快響應(yīng)速度,提高用戶體驗(yàn)
合理利用CPU內(nèi)存
統(tǒng)一管理
3.線程池使用的場合
服務(wù)器接受大量請求時(shí),使用線程池技術(shù)是非常合適的,它可以大大減少線程的創(chuàng)建和銷毀次數(shù),提高服務(wù)器的工作效率。在實(shí)際開發(fā)中,如果創(chuàng)建5個(gè)以上 的線程,那么就可以使用線程池來管理線程。
4.創(chuàng)建和停止線程
線程池構(gòu)造方法的參數(shù)?
線程池應(yīng)該手動創(chuàng)建和自動創(chuàng)建那個(gè)更好?
線程池里的線程數(shù)量設(shè)置未多少合適?
停止線程的正確方法?
線程池構(gòu)造函數(shù)的參數(shù):
corePoolSize: 核心線程數(shù)
線程池在完成初始化后,默認(rèn)情況下,線程池中并沒有任何線程,會等到有任務(wù)到來時(shí)再去創(chuàng)建新的線程去執(zhí)行任務(wù)。
maxPoolSize:在核心線程的基礎(chǔ)上,額外增加的線程數(shù)的上限。
根據(jù)圖可知添加線程的規(guī)則:
1.如果線程數(shù)小于corePoolSize,即使其他工作線程處于空閑狀態(tài),也會創(chuàng)建一個(gè)新線程來運(yùn)行任務(wù)。
2.如果線程數(shù)等于或大于corePoolSize但少于maximumPoolSize,則將任務(wù)放入隊(duì)列。
3.如果線程池已滿,并且線程數(shù)小于maxPoolSize,則創(chuàng)建一個(gè)新線程來運(yùn)行任務(wù)。
4.如果隊(duì)列已滿,并且線程數(shù)大于或等于maxPoolSzie,則參數(shù)拒絕該任務(wù)。
添加線程判斷順序:corePoolSize——workQueue——maxPoolSize
比如線程池的核心線程是5個(gè),最大線程池大小為10個(gè),隊(duì)列為50個(gè)。
則線程池的請求最多會創(chuàng)建5個(gè),然后任務(wù)將被添加到隊(duì)列中,直到達(dá)到50。隊(duì)列已滿時(shí),將創(chuàng)建最新的線程maxPoolSize,最多達(dá)到10個(gè),如果再來任務(wù)就直接拒絕。
keepAliveTime:如果線程池當(dāng)前的線程數(shù)多于corePoolSize,那么如果多余的線程空閑時(shí)間超過keepAliveTime,那么就會終止。
ThreadFactory:
默認(rèn)使用Executors.defaultThreadFactory()
創(chuàng)建出來的線程都在同一個(gè)線程組。
如果自己指定ThreadFactory,那么就可以改變線程名、線程組、優(yōu)先級、是否是守護(hù)線程等等。
常見的3中隊(duì)列類型:
直接交接:SynchronousQueue
無界隊(duì)列:LinkedBlockingQueue
有界隊(duì)列:ArrayBlockingQueue
線程池應(yīng)該手動創(chuàng)建和自動創(chuàng)建那個(gè)更好?
手動創(chuàng)建好,因?yàn)檫@樣可以明確線程池的運(yùn)行規(guī)則和避開資源浪費(fèi)的風(fēng)險(xiǎn)。
- newFixedThreadPool:容易造成大量內(nèi)存占用,可能導(dǎo)致DOM
- newSingleThreadExecutor:當(dāng)請求堆積的時(shí)候,可能會占用大量內(nèi)存。
public class FixedThreadPoolTest { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(4); for (int i = 0; i < 500; i++) { executorService.execute(new Task()); } } } class Task implements Runnable{ @Override public void run() { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()); } }
- newCachedThreadPool:弊端在于第二個(gè)參數(shù)maximumPoolSize被設(shè)置為了Integer.MAX_VALUE,這可能會創(chuàng)建數(shù)量非常多的線程,甚至導(dǎo)致DOM
- newScheduledThreadPool:原因和newCachedThreadPool一樣
//演示FixedThreadPool出錯 public class FixedThreadPoolOOM { private static ExecutorService executorService = Executors.newFixedThreadPool(1); public static void main(String[] args) { for (int i = 0; i < Integer.MAX_VALUE; i++) { executorService.execute(new SubThread()); } } } class SubThread implements Runnable{ @Override public void run() { try { Thread.sleep(10000000); } catch (InterruptedException e) { e.printStackTrace(); } } }
常見的線程池:
FixedThreadPool
CachedThreadPool
:可緩存線程池,具有自動回收多余線程的功能
ScheduledThreadPool
:支持定時(shí)及周期性任務(wù)執(zhí)行的線程池
SingleThreadExecutor
:單線程的線程池只會用唯一的工作線程來執(zhí)行任務(wù)
原理和FixedThreadPool一樣,但是線程數(shù)量被設(shè)為1
四種線程池的構(gòu)造方法的參數(shù):
阻塞隊(duì)列分析:
5.停止線程池的方法
shutdown
:只是將線程池的狀態(tài)設(shè)置為 shutdown 狀態(tài),但任務(wù)并沒有中斷,還是會繼續(xù)執(zhí)行下去。此時(shí)線程池不會接受新的任務(wù),只是將原有的任務(wù)執(zhí)行結(jié)束。shutdownNow
:將線程池的狀態(tài)設(shè)置為STOP,正在執(zhí)行的任務(wù)會停止,沒被執(zhí)行的任務(wù)會被返回。isShutdown
:當(dāng)調(diào)用shutdown()或shutdownNow()方法后返回為true,否則返回為false。isTerminated
:線程任務(wù)全部執(zhí)行完返回trueawaitTerminated
:有兩個(gè)參數(shù),第一個(gè)是long類型的數(shù)值,第二個(gè)是時(shí)間類型TimeUnit,用于設(shè)置阻塞時(shí)間。它是一個(gè)阻塞的方法,若線程池一直運(yùn)行則會一直阻塞,直到線程池關(guān)閉返回true,或阻塞時(shí)間超過你設(shè)置的這個(gè)時(shí)間,則返回false。此方法必須放在shutdown()方法之后,否則一直在阻塞,或超過設(shè)置的阻塞時(shí)間返回false。
//演示關(guān)閉線程池 public class ShutDown { public static void main(String[] args) throws InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(10); for (int i = 0; i < 500; i++) { executorService.execute(new ShutDownTask()); } Thread.sleep(1500); // executorService.shutdown(); // System.out.println(executorService.isShutdown()); executorService.awaitTermination(3L, TimeUnit.SECONDS); } } class ShutDownTask implements Runnable{ @Override public void run() { try { Thread.sleep(500); System.out.println(Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } }
6.暫停和恢復(fù)線程池
//暫停線程池 pauseAbleThreadPool.pause(); //恢復(fù)線程池 pauseAbleThreadPool.resume();
代碼實(shí)現(xiàn):
//演示每個(gè)任務(wù)執(zhí)行前后放鉤子函數(shù) public class PauseAbleThreadPool extends ThreadPoolExecutor { private final ReentrantLock lock = new ReentrantLock(); private Condition unpaused = lock.newCondition(); private boolean isPaused; public PauseAbleThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); } public PauseAbleThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory); } public PauseAbleThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler); } public PauseAbleThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler); } @Override protected void beforeExecute(Thread t, Runnable r) { super.beforeExecute(t, r); lock.lock(); try { while (isPaused) { unpaused.await(); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } private void pause() { lock.lock(); try { isPaused = true; } finally { lock.unlock(); } } public void resume() { lock.lock(); try { isPaused = false; unpaused.signalAll(); } finally { lock.unlock(); } } public static void main(String[] args) throws InterruptedException { PauseAbleThreadPool pauseAbleThreadPool = new PauseAbleThreadPool(10, 20, 10l, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); Runnable runnable = new Runnable() { @Override public void run() { System.out.println("我被執(zhí)行"); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } }; for (int i = 0; i < 10000; i++) { pauseAbleThreadPool.execute(runnable); } Thread.sleep(1500); pauseAbleThreadPool.pause(); System.out.println("線程池被暫停了"); Thread.sleep(1500); pauseAbleThreadPool.resume(); System.out.println("線程池被恢復(fù)了"); } }
實(shí)現(xiàn)原理及源碼分析:
線程池的組成部分:
- 線程池管理器
- 工作線程
- 任務(wù)隊(duì)列
- 任務(wù)接口(Task)
到此這篇關(guān)于Java線程池由淺入深掌握到精通的文章就介紹到這了,更多相關(guān)Java 線程池內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot項(xiàng)目配置明文密碼泄露問題的處理方式
這篇文章主要介紹了SpringBoot項(xiàng)目配置明文密碼泄露問題的處理方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06IDEA 中創(chuàng)建Spring Data Jpa 項(xiàng)目的示例代碼
這篇文章主要介紹了IDEA 中創(chuàng)建Spring Data Jpa 項(xiàng)目的示例代碼,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04java循環(huán)練習(xí)的簡單代碼實(shí)例
本篇文章介紹了,java中循環(huán)練習(xí)的一些簡單代碼實(shí)例。需要的朋友參考下2013-04-04后端報(bào)TypeError:Cannot?read?properties?of?null?(reading?‘
這篇文章主要給大家介紹了關(guān)于后端報(bào)TypeError:Cannot?read?properties?of?null?(reading?‘xxx‘)錯誤的解決辦法,這個(gè)錯誤是開發(fā)中常見的錯誤之一,需要的朋友可以參考下2023-05-05Spring?Boot?@Autowired?@Resource屬性賦值時(shí)機(jī)探究
這篇文章主要為大家介紹了Spring?Boot?@Autowired?@Resource屬性賦值時(shí)機(jī),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07解決springboot整合cxf啟動報(bào)錯,原因是版本問題
這篇文章主要介紹了解決springboot整合cxf啟動報(bào)錯,原因是版本問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07Java?guava框架LoadingCache及CacheBuilder本地小容量緩存框架總結(jié)
Guava?Cache本地緩存框架主要是一種將本地?cái)?shù)據(jù)緩存到內(nèi)存中,但數(shù)據(jù)量并不能太大,否則將會占用過多的內(nèi)存,本文給大家介紹Java?guava框架?LoadingCache及CacheBuilder?本地小容量緩存框架總結(jié),感興趣的朋友一起看看吧2023-12-12java設(shè)計(jì)模式之組合模式(Composite)
這篇文章主要為大家詳細(xì)介紹了java設(shè)計(jì)模式之組合模式Composite,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01