Java線程池由淺入深掌握到精通
1.為什么使用線程池?
反復創(chuàng)建線程開銷大,可以復用線程池
過多的線程會占用太多的內(nèi)存
解決以上問題的方法:
- 用少量的線程,避免內(nèi)存占用過多
- 讓這部分線程都保持工作,且反復執(zhí)行任務,避免生命周期的損耗
2.線程池的好處:
加快響應速度,提高用戶體驗
合理利用CPU內(nèi)存
統(tǒng)一管理
3.線程池使用的場合
服務器接受大量請求時,使用線程池技術(shù)是非常合適的,它可以大大減少線程的創(chuàng)建和銷毀次數(shù),提高服務器的工作效率。在實際開發(fā)中,如果創(chuàng)建5個以上 的線程,那么就可以使用線程池來管理線程。
4.創(chuàng)建和停止線程
線程池構(gòu)造方法的參數(shù)?
線程池應該手動創(chuàng)建和自動創(chuàng)建那個更好?
線程池里的線程數(shù)量設置未多少合適?
停止線程的正確方法?
線程池構(gòu)造函數(shù)的參數(shù):
corePoolSize: 核心線程數(shù)
線程池在完成初始化后,默認情況下,線程池中并沒有任何線程,會等到有任務到來時再去創(chuàng)建新的線程去執(zhí)行任務。
maxPoolSize:在核心線程的基礎(chǔ)上,額外增加的線程數(shù)的上限。
根據(jù)圖可知添加線程的規(guī)則:
1.如果線程數(shù)小于corePoolSize,即使其他工作線程處于空閑狀態(tài),也會創(chuàng)建一個新線程來運行任務。
2.如果線程數(shù)等于或大于corePoolSize但少于maximumPoolSize,則將任務放入隊列。
3.如果線程池已滿,并且線程數(shù)小于maxPoolSize,則創(chuàng)建一個新線程來運行任務。
4.如果隊列已滿,并且線程數(shù)大于或等于maxPoolSzie,則參數(shù)拒絕該任務。
添加線程判斷順序:corePoolSize——workQueue——maxPoolSize
比如線程池的核心線程是5個,最大線程池大小為10個,隊列為50個。
則線程池的請求最多會創(chuàng)建5個,然后任務將被添加到隊列中,直到達到50。隊列已滿時,將創(chuàng)建最新的線程maxPoolSize,最多達到10個,如果再來任務就直接拒絕。
keepAliveTime:如果線程池當前的線程數(shù)多于corePoolSize,那么如果多余的線程空閑時間超過keepAliveTime,那么就會終止。
ThreadFactory:
默認使用Executors.defaultThreadFactory()
創(chuàng)建出來的線程都在同一個線程組。
如果自己指定ThreadFactory,那么就可以改變線程名、線程組、優(yōu)先級、是否是守護線程等等。
常見的3中隊列類型:
直接交接:SynchronousQueue
無界隊列:LinkedBlockingQueue
有界隊列:ArrayBlockingQueue
線程池應該手動創(chuàng)建和自動創(chuàng)建那個更好?
手動創(chuàng)建好,因為這樣可以明確線程池的運行規(guī)則和避開資源浪費的風險。
- newFixedThreadPool:容易造成大量內(nèi)存占用,可能導致DOM
- newSingleThreadExecutor:當請求堆積的時候,可能會占用大量內(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:弊端在于第二個參數(shù)maximumPoolSize被設置為了Integer.MAX_VALUE,這可能會創(chuàng)建數(shù)量非常多的線程,甚至導致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
:支持定時及周期性任務執(zhí)行的線程池
SingleThreadExecutor
:單線程的線程池只會用唯一的工作線程來執(zhí)行任務
原理和FixedThreadPool一樣,但是線程數(shù)量被設為1
四種線程池的構(gòu)造方法的參數(shù):
阻塞隊列分析:
5.停止線程池的方法
shutdown
:只是將線程池的狀態(tài)設置為 shutdown 狀態(tài),但任務并沒有中斷,還是會繼續(xù)執(zhí)行下去。此時線程池不會接受新的任務,只是將原有的任務執(zhí)行結(jié)束。shutdownNow
:將線程池的狀態(tài)設置為STOP,正在執(zhí)行的任務會停止,沒被執(zhí)行的任務會被返回。isShutdown
:當調(diào)用shutdown()或shutdownNow()方法后返回為true,否則返回為false。isTerminated
:線程任務全部執(zhí)行完返回trueawaitTerminated
:有兩個參數(shù),第一個是long類型的數(shù)值,第二個是時間類型TimeUnit,用于設置阻塞時間。它是一個阻塞的方法,若線程池一直運行則會一直阻塞,直到線程池關(guān)閉返回true,或阻塞時間超過你設置的這個時間,則返回false。此方法必須放在shutdown()方法之后,否則一直在阻塞,或超過設置的阻塞時間返回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.暫停和恢復線程池
//暫停線程池 pauseAbleThreadPool.pause(); //恢復線程池 pauseAbleThreadPool.resume();
代碼實現(xiàn):
//演示每個任務執(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("線程池被恢復了"); } }
實現(xiàn)原理及源碼分析:
線程池的組成部分:
- 線程池管理器
- 工作線程
- 任務隊列
- 任務接口(Task)
到此這篇關(guān)于Java線程池由淺入深掌握到精通的文章就介紹到這了,更多相關(guān)Java 線程池內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IDEA 中創(chuàng)建Spring Data Jpa 項目的示例代碼
這篇文章主要介紹了IDEA 中創(chuàng)建Spring Data Jpa 項目的示例代碼,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-04-04后端報TypeError:Cannot?read?properties?of?null?(reading?‘
這篇文章主要給大家介紹了關(guān)于后端報TypeError:Cannot?read?properties?of?null?(reading?‘xxx‘)錯誤的解決辦法,這個錯誤是開發(fā)中常見的錯誤之一,需要的朋友可以參考下2023-05-05Spring?Boot?@Autowired?@Resource屬性賦值時機探究
這篇文章主要為大家介紹了Spring?Boot?@Autowired?@Resource屬性賦值時機,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-07-07Java?guava框架LoadingCache及CacheBuilder本地小容量緩存框架總結(jié)
Guava?Cache本地緩存框架主要是一種將本地數(shù)據(jù)緩存到內(nèi)存中,但數(shù)據(jù)量并不能太大,否則將會占用過多的內(nèi)存,本文給大家介紹Java?guava框架?LoadingCache及CacheBuilder?本地小容量緩存框架總結(jié),感興趣的朋友一起看看吧2023-12-12