[线程池] ------ 形象的描述线程池,用一个特好记的例子来记忆
線程池
為了減少線程頻繁的創建和銷毀過程,引入池的概念。
將一些線程先創建好放在線程池中,每次來任務就用池中的線程執行,空閑時池中線程就等待,但不銷毀。
原始線程池的創建:
ThreadPoolExecutor executor1 = new ThreadPoolExecutor(入參);
入參有4種方式,分別對上面參數的賦值:threadFactory和handler不寫就是用默認的
(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue workQueue);
(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory);
(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue workQueue, RejectedExecutionHandler handler);
(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler);
線程池的主要參數
corePoolSize:核心線程池的大小,默認最開始線程池是沒有線程的,只有當有任務來的時候會創建線程,然后這個線程以后就不會銷毀了,會一直待在線程池中等待任務。
maximumPoolSize:最大線程數。表示線程池最多可以創建多少個線程,當任務隊列放滿后還有新任務時,會嘗試將線程數提高到最大線程數。
keepAliveTime:非核心線程空閑銷毀倒計時,即當線程池中的線程數,大于核心線程數時,針對多出來的線程(就叫非核心線程),如果空閑時間超過這個時間,就銷毀。
unit:keepAliveTime參數的單位,可以是:
workQueue:阻塞隊列,存放要等待的任務,也叫任務隊列。可以是:
ArrayBlockingQueue LinkedBlockingQueue SynchronousQueuethreadFactory:線程工廠,用來創建線程。
handler:拒絕任務的策略,當線程數達到maximumPoolSize,并且workQueue也被任務放滿時,再來就會被拒絕,可選的拒絕策略有:
我們舉個實際例子來記憶這些參數(重要)
例子的場景是火車票售票大廳,乘客買票的例子。
火車票售票大廳,往往會開多個窗口來賣票,我們把一個個乘客比作是一個個任務,把一個個售票窗口比作一個個線程。
假設某縣火車票售票大廳,第一天營業,剛上班,一個窗口也沒開,這就是線程池的初始化。
這時候來了第一位乘客a,同時喚醒第一個售票窗口A,開始辦理業務。
此時馬上又來了一個乘客b,窗口A正在被占用,于是又開啟第二個窗口B為乘客b辦理業務。
我們已知這個火車站就2個正式窗口,相當于corePoolSize=2。
那么后面來的人就要開始等了,我們把售票大廳的凳子比作任務隊列,比如只有3個凳子,即任務隊列最多容納3個任務。
與此同時,前面的乘客ab都沒辦理完,所以窗口A和窗口B都被占用了,后面又連續來了3個乘客cde,剛好,cde都坐在了凳子上,那么凳子就被坐滿了。
此時,fg二位乘客也跟著來了,那么此時2個正式窗口被占,3個凳子也被占,于是火車站管理員臨時又加了兩個窗口,
這兩個臨時窗口,就相當于非核心線程,前面那兩個AB窗口,就是正式窗口,就是核心線程,總共最多開4個窗口,那么就有maximumPoolSize=4。
注意了,這里是等到正式窗口被占完,凳子也被坐滿,才開的臨時窗口。即核心線程占完,隊列填滿,才會嘗試使用非核心線程做任務。
此時7位乘客,算是把這個售票大廳的資源給用完了,后面又來了乘客h,他就進不去了,要么把他趕出去算是拋個異常,要么他就一直著嘗試進去算是重試,等等拒絕他的策略,這就用到了拒絕策略。
后來慢慢的,前面的乘客都把票買了,凳子也空了,臨時窗口也空閑了,等了1個小時也沒有任務了,管理員就把臨時窗口又撤銷了,只留兩個正式窗口。這個1小時就是keepAliveTime=1,單位unit是小時TimeUnit.HOURS。
常用的線程池使用方式:
常用的線程池,不用new ThreadPoolExecutor(入參)的方式創建,是用Executors來創建的。
可回收緩存線程池(corePoolSize = 0;maximumPoolSize = Integer.MAX_VALUE;線程空閑后都可以銷毀,但要注意設置合適的回收時間)
Executors.newCachedThreadPool();
繼續上面的火車站的例子,這個情況就相當于火車站沒有正式窗口,全是臨時窗口,有人就開臨時窗口,沒人就關了窗口。
固定線程池(corePoolSize = maximumPoolSize)
Executors.newFixedThreadPool(n);
這個情況相當于沒有臨時窗口了,但是凳子特別多,即workQueue.size() = Integer.MAX_VALUE ,正式窗口和凳子都滿后,再來的人就直接拒絕策略給拒絕了。
單線程線程池(corePoolSize = maximumPoolSize = 1,串行用)
Executors.newSingleThreadExecutor();
這個情況就是只有一個正式窗口,其他的和固定線程池一樣。
可調度線程池(支持定時與周期性任務)
Executors.newScheduledThreadPool(n);
實戰例子
package com.zs.test;import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import java.util.concurrent.*;/*** @author zhangshuai**/ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring-config-test.xml") public class TestThreadPool {Logger logger = LoggerFactory.getLogger(TestThreadPool.class);final static CountDownLatch countDownLatch = new CountDownLatch(14);private ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 6, 3, TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(5));private ExecutorService executor2 = Executors.newCachedThreadPool();private ExecutorService executor3 = Executors.newSingleThreadExecutor();private ExecutorService executor4 = Executors.newFixedThreadPool(3);private ExecutorService executor5 = Executors.newScheduledThreadPool(2);@Testpublic void test() throws InterruptedException {for(int i=0;i<14;i++){MyTestTask myTestTask = new MyTestTask(i);executor.execute(myTestTask);logger.info("線程池中線程數目:"+executor.getPoolSize()+",隊列中等待執行的任務數目:"+executor.getQueue().size()+",已執行完別的任務數目:"+executor.getCompletedTaskCount());if(i == 6){//總共14個任務,核心線程數設置5個,最大線程數6個,// 我們讓前7個任務執行完等待10秒,是為了讓設置的銷毀非核心線程的時間3秒起作用。//后面就能看到非核心線程的創建,銷毀和再次創建。Thread.sleep(10000);}}executor.shutdown();countDownLatch.await();}class MyTestTask implements Runnable {private int taskNum;public MyTestTask(int num) {this.taskNum = num;}@Overridepublic void run() {try {logger.info("正在執行的taskNum: "+taskNum);try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}logger.info("task: "+taskNum+"執行完畢");} catch (Exception e) {e.printStackTrace();}finally{countDownLatch.countDown();}}} }執行完的結果是:
先創建5個線程執行5個任務01234
3秒后01234執行完
接著創建56任務
與此同時主線程執行sleep10秒
3秒后56任務執行完
當主線程醒來,再次執行后面的任務。
總結
以上是生活随笔為你收集整理的[线程池] ------ 形象的描述线程池,用一个特好记的例子来记忆的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: caj转pdf python_PDF怎么
- 下一篇: 9 10次c语言上机作业答案,C语言第五