久久午夜无码,国产中文资源,Chinese熟女熟妇2乱2,高清五码d一区

線程池的工作原理(一文詳解線程池的工作原理)

線程池的工作原理(一文詳解線程池的工作原理)

尚念夢(mèng) 2025-04-15 科技 21 次瀏覽 0個(gè)評(píng)論

在工作中或多或少都使用過(guò)線程池。但是為什么要使用線程池呢?從它的名稱(chēng)中我們就可以猜到,線程池是使用了一種池化技術(shù)(Pooling Technology)。和很多其他池化技術(shù)一樣,都是為了更高效的利用資源,例如連接池,內(nèi)存池等。

數(shù)據(jù)庫(kù)連接是一種很昂貴的資源,創(chuàng)建和銷(xiāo)毀都需要付出高昂的代價(jià)。為了避免頻繁地創(chuàng)建數(shù)據(jù)庫(kù)連接,所以產(chǎn)生了數(shù)據(jù)庫(kù)連接池技術(shù)。優(yōu)先在池子中創(chuàng)建一批數(shù)據(jù)庫(kù)連接,當(dāng)有需要訪問(wèn)數(shù)據(jù)庫(kù)時(shí),直接到池子中去獲取一個(gè)可用的連接,使用完了之后再歸還到連接池中去。

同樣的,線程也是一種很寶貴的資源,并且也是一種有限的資源,創(chuàng)建和銷(xiāo)毀線程也同樣需要付出不菲的代價(jià)。我們所有的代碼執(zhí)行都是由一個(gè)一個(gè)的線程支撐起來(lái)的,如今的芯片架構(gòu)也決定了我們必須編寫(xiě)多線程執(zhí)行的程序,以獲得最高的程序性能。那么怎樣高效地管理多線程之間的分工與協(xié)作就成了一個(gè)關(guān)鍵問(wèn)題,Doug Lea大神為我們?cè)O(shè)計(jì)并實(shí)現(xiàn)了一款線程池工具,通過(guò)該工具就可以實(shí)現(xiàn)多線程的能力,并實(shí)現(xiàn)任務(wù)的高效執(zhí)行與調(diào)度。為了正確合理地使用線程池工具,我們有必要對(duì)線程池的原理進(jìn)行了解。

了解線程池工作原理主要有三個(gè)方面:線程池狀態(tài)、線程池的重要屬性和線程池的工作流程。

線程池狀態(tài)

線程池是有狀態(tài)的,這些狀態(tài)標(biāo)識(shí)這個(gè)線程池內(nèi)部的一些運(yùn)行情況。線程池的開(kāi)啟到關(guān)閉的過(guò)程就是線程池狀態(tài)的一個(gè)流轉(zhuǎn)過(guò)程。

線程池共有5種狀態(tài):

一文詳解線程池的工作原理

運(yùn)行狀態(tài)(RUNNING):此狀態(tài)下,線程池可以接受新的任務(wù),也可以處理阻塞隊(duì)列中的任務(wù)。執(zhí)行shutdown()方法可進(jìn)入待關(guān)閉(SHUTDOWN)狀態(tài),執(zhí)行shutdownNow()方法可進(jìn)入停止(STOP)狀態(tài)。待關(guān)閉狀態(tài)(SHUTDOWN):此狀態(tài)下,線程池不再接受新的任務(wù),繼續(xù)處理阻塞隊(duì)列中的任務(wù)。當(dāng)阻塞隊(duì)列中的任務(wù)為空,且工作線程數(shù)為0的時(shí)候,進(jìn)入整理(TIDYING)狀態(tài)。停止?fàn)顟B(tài)(STOP):此狀態(tài)下,線程池不接受新任務(wù),也不處理阻塞隊(duì)列中的任務(wù),反而會(huì)嘗試結(jié)束執(zhí)行中的任務(wù)。當(dāng)工作線程數(shù)為0時(shí),進(jìn)入整理(TIDYING)狀態(tài)。整理狀態(tài)(TIDYING):此狀態(tài)下,所有任務(wù)都已經(jīng)執(zhí)行完畢,且沒(méi)有工作線程。執(zhí)行terminated()方法進(jìn)入終止(TERMINATED)狀態(tài)。終止?fàn)顟B(tài)(TERMINATED):此狀態(tài)下,線程池完全終止,并完成了所有資源的釋放。線程池的重要屬性

一個(gè)線程池的核心參數(shù)有很多,每個(gè)參數(shù)都有著特殊的作用,各個(gè)參數(shù)聚合再一起后將完成整個(gè)線程池的完整工作。其中的六個(gè)尤為重要:線程狀態(tài)和工作線程的數(shù)量,核心線程數(shù)和最大線程數(shù),創(chuàng)建線程的工廠,緩存任務(wù)的阻塞隊(duì)列,非核心線程存活的時(shí)間和拒絕策略。

線程狀態(tài)和工作線程數(shù)量

首先線程池是有狀態(tài)的,在不同的狀態(tài)下,線程池的行為是不一樣的。

然后線程池肯定是需要線程去執(zhí)行具體的任務(wù),所以在線程池中就封裝了一個(gè)內(nèi)部類(lèi)Worker作為工作線程,每個(gè)Worker中都維持著一個(gè)Thread。

線程池的重點(diǎn)之一,就是控制線程資源合理高效的使用,所以必須控制工作線程的個(gè)數(shù),所以需要保存當(dāng)前線程池中工作線程的個(gè)數(shù)。

看到這里,你是否覺(jué)得需要用兩個(gè)變量來(lái)保存線程池的狀態(tài)和線程池中工作線程的個(gè)數(shù)呢?但是在ThreadPoolExecutor中只用了一個(gè)AtomicInteger型的變量就保存了這兩個(gè)屬性的值,那就是ctl。

一文詳解線程池的工作原理

ctl是一個(gè)原子操作類(lèi)型(AtomicInteger)的變量。ctl的高3位用來(lái)表示線程池的狀態(tài)(runState),低29位用來(lái)表示工作線程的個(gè)數(shù)(workerCnt)。為什么要用3位來(lái)表示線程池的狀態(tài)呢,原因是因?yàn)榫€程池一共有5種狀態(tài),而2位只能表示出4種情況(2位是2^2,最多產(chǎn)生4種結(jié)果),至少需要3位才能表示得了全部的5種狀態(tài)(3位是3^2,最多產(chǎn)生9種結(jié)果)。

核心線程數(shù)和最大線程數(shù)

現(xiàn)在有了標(biāo)識(shí)工作線程的個(gè)數(shù)的變量了,那到底該有多少個(gè)線程才合適呢?線程多了會(huì)浪費(fèi)線程資源,少了又不能發(fā)揮線程池的性能。

為了解決這個(gè)問(wèn)題,線程池設(shè)計(jì)了兩個(gè)變量來(lái)協(xié)作,分別是:

核心線程數(shù)(corePoolSize):用來(lái)表示線程池中的核心線程的數(shù)量,也可以稱(chēng)為可閑置的線程數(shù)量。

最大線程數(shù)(maximumPoolSize):用來(lái)表示線程池中最多能夠創(chuàng)建的線程數(shù)量。

現(xiàn)在我們有一個(gè)疑惑,既然已經(jīng)有了標(biāo)識(shí)工作線程的個(gè)數(shù)的變量了,為什么還要有核心線程數(shù)和最大線程數(shù)呢?

其實(shí)你這樣想就能夠理解了,創(chuàng)建線程是有代價(jià)的,不能每次要執(zhí)行一個(gè)任務(wù)時(shí)就創(chuàng)建一個(gè)線程,但是也不能在任務(wù)非常多的時(shí)候,只有少量的線程在執(zhí)行,這樣任務(wù)是來(lái)不及處理的,而是應(yīng)該創(chuàng)建合適的足夠多的線程來(lái)及時(shí)地處理任務(wù)。

隨著任務(wù)數(shù)量的變化,當(dāng)任務(wù)數(shù)量明顯減少時(shí),原本創(chuàng)建的多余的線程就沒(méi)有必要再存活著了,因?yàn)檫@時(shí)使用少量的線程就能夠處理得過(guò)來(lái)了,所以說(shuō)真正工作的線程的數(shù)量,是隨著任務(wù)的變化而變化的。

那核心線程數(shù)和最大線程數(shù)和工作線程個(gè)數(shù)的關(guān)系是什么呢?

一文詳解線程池的工作原理

工作線程的個(gè)數(shù)可能從0到最大線程數(shù)之間變化,當(dāng)執(zhí)行一段時(shí)間之后可能維持在核心線程數(shù)(corePoolSize),但也不是絕對(duì)的,取決于核心線程是否允許被超時(shí)回收。

創(chuàng)建線程的工廠

既然是線程池,那自然少不了線程。線程該如何來(lái)創(chuàng)建呢?這個(gè)任務(wù)就交給了線程工廠ThreadFactory來(lái)完成。

緩存任務(wù)的阻塞隊(duì)列

上面我們說(shuō)了核心線程數(shù)和最大線程數(shù),并且也介紹了工作線程的個(gè)數(shù)是在0和最大線程數(shù)之間變化的。但是不可能一下子就創(chuàng)建了所有線程,把線程池裝滿,而是有一個(gè)過(guò)程:

當(dāng)線程池接受到一個(gè)任務(wù)時(shí),如果工作線程數(shù)沒(méi)有達(dá)到corePoolSize,那么就會(huì)新建一個(gè)線程,并綁定該任務(wù),知道工作線程的數(shù)量達(dá)到corePoolSize前都不會(huì)重用之前創(chuàng)建的線程。

當(dāng)工作線程數(shù)達(dá)到corePoolSize了,這是又接收到新任務(wù)時(shí),會(huì)將任務(wù)存放在一個(gè)阻塞隊(duì)列(workQueue)中等待核心線程去執(zhí)行。為什么不直接創(chuàng)建更多的線程來(lái)執(zhí)行新任務(wù)呢?原因是核心線程中很可能已經(jīng)有線程執(zhí)行完自己的任務(wù)了,或者有其他線程馬上就能處理完當(dāng)前的任務(wù),并且接下來(lái)就能投入到新的任務(wù)中去,所以阻塞隊(duì)列是一種緩沖機(jī)制,給核心線程一個(gè)機(jī)會(huì)讓他們充分發(fā)揮自己的能力。另外一個(gè)值得考慮的原因是,創(chuàng)建線程畢竟是代價(jià)昂貴的,不可能一有任務(wù)要執(zhí)行就去創(chuàng)建一個(gè)新的線程。

所以我們需要為線程池配備一個(gè)阻塞隊(duì)列,用來(lái)臨時(shí)緩存任務(wù),這些任務(wù)將等待工作線程來(lái)執(zhí)行。

一文詳解線程池的工作原理

非核心線程存活時(shí)間

上面我們說(shuō)了,當(dāng)工作線程數(shù)達(dá)到corePoolSize時(shí),線程池會(huì)將新接收到的任務(wù)放在阻塞隊(duì)列中,而阻塞隊(duì)列又分為兩種情況:一種是有界的隊(duì)列,一種是無(wú)界的隊(duì)列。

如果是無(wú)界隊(duì)列,那么當(dāng)核心線程都在忙時(shí),所有新提交的任務(wù)都會(huì)被存放在該無(wú)界隊(duì)列中,這時(shí)最大線程數(shù)將變得沒(méi)有意義,因?yàn)樽枞?duì)列不會(huì)存在被裝滿的情況。

如果是有界隊(duì)列,那么當(dāng)阻塞隊(duì)列中裝滿了等待執(zhí)行的任務(wù),這時(shí)再有新任務(wù)提交時(shí),線程池就需要?jiǎng)?chuàng)建新的臨時(shí)線程來(lái)處理,相當(dāng)于增派人手來(lái)處理任務(wù)。

但是創(chuàng)建的臨時(shí)線程是有存活時(shí)間的,不可能讓它們一直都存活著,當(dāng)阻塞隊(duì)列中的任務(wù)被執(zhí)行完畢,并且又沒(méi)有那么多新任務(wù)被提交時(shí),臨時(shí)線程就需要被回收銷(xiāo)毀,而在被回收銷(xiāo)毀之前等待的這段時(shí)間,就是非核心線程的存活時(shí)間,也就是keepAliveTime屬性。

那么什么是非核心線程呢?是不是先創(chuàng)建的線程就是核心線程,后創(chuàng)建的就是非核心線程呢?

其實(shí)核心線程跟創(chuàng)建的先后沒(méi)有關(guān)系,而是跟工作線程的個(gè)數(shù)有關(guān),如果當(dāng)前工作線程的個(gè)數(shù)大于核心線程數(shù),那么所有的線程都可能是非核心線程,都有被回收的可能。

一個(gè)線程執(zhí)行完一個(gè)任務(wù)后,會(huì)去阻塞隊(duì)列里面取新的任務(wù),在取到任務(wù)之前,它就是一個(gè)閑置的線程。

取任務(wù)的方法有兩種,一種是通過(guò)take()方法一直阻塞直到取出任務(wù),另一種是通過(guò)poll(keepAliveTime, timeUnit)方法在一定時(shí)間內(nèi)取出任務(wù)或者超時(shí),如果超時(shí)這個(gè)線程就會(huì)被回收,請(qǐng)注意核心線程一般不會(huì)被回收。

那么怎么保證核心線程不會(huì)被回收呢?還是跟工作線程的個(gè)數(shù)有關(guān),每一個(gè)線程在取任務(wù)的時(shí)候,線程池會(huì)比較當(dāng)前的工作線程個(gè)數(shù)與核心線程數(shù)。

如果工作線程數(shù)小于當(dāng)前的核心線程數(shù),則使用第一種方法取任務(wù),也就是沒(méi)有超時(shí)回收,這時(shí)所有的工作線程都是核心線程,它們不會(huì)被回收。如果工作線程數(shù)大于核心線程數(shù),則使用第二種方法取任務(wù),一旦超時(shí)就回收,所以并沒(méi)有絕對(duì)的核心線程,只要這個(gè)線程沒(méi)有在存活時(shí)間內(nèi)取到任務(wù)去執(zhí)行就會(huì)被回收。

所以每個(gè)線程如果想要保住自己核心線程的身份,必須充分努力,盡可能快得獲取到任務(wù)去執(zhí)行,這樣才能避免被回收的命運(yùn)。

核心線程一般不會(huì)被回收,但是也不是絕對(duì)的,如果我們?cè)O(shè)置了允許核心線程超時(shí)被回收的話,那么就沒(méi)有核心線程這種說(shuō)法了,所有的線程都會(huì)通過(guò)poll(keepAliveTime, timeUnit)來(lái)獲取任務(wù),一旦超時(shí)獲取不到任務(wù),就會(huì)被回收,一般很少會(huì)這樣來(lái)使用,除非該線程池需要處理的任務(wù)非常少,并且頻率也不高,不需要將核心線程一直維持著。

拒絕策略

雖然我們有了阻塞隊(duì)列來(lái)對(duì)任務(wù)進(jìn)行緩存,從一定程度上為線程池的執(zhí)行提供了緩沖期,但是如果是有界的阻塞隊(duì)列,那就存在隊(duì)列滿的情況,也存在工作線程的數(shù)據(jù)已經(jīng)達(dá)到最大線程數(shù)的時(shí)候。如果這時(shí)候再有新的任務(wù)提交時(shí),顯然線程池已經(jīng)心有余而力不足了,因?yàn)榧葲](méi)有空余的隊(duì)列空間來(lái)存放該任務(wù),也無(wú)法創(chuàng)建新的線程來(lái)執(zhí)行該任務(wù)了,所以這時(shí)我們就需要有一種拒絕策略,即handler。

拒絕策略是一個(gè)RejectedExecutionHandler類(lèi)型的變量,用戶(hù)可以自行指定拒絕的策略,如果不指定的話,線程池將使用默認(rèn)的拒絕策略:拋出異常。

在線程池中還為我們提供了很多其他可以選擇的拒絕策略:

直接丟棄該任務(wù)使用調(diào)用者線程執(zhí)行該任務(wù)丟棄任務(wù)隊(duì)列中的最老的一個(gè)任務(wù),然后提交該任務(wù)工作流程

了解了線程池中所有的重要屬性之后,現(xiàn)在我們需要來(lái)了解下線程池的工作流程了。

一文詳解線程池的工作原理

上面是一張線程池工作的精簡(jiǎn)圖,實(shí)際的過(guò)程要比這個(gè)復(fù)雜得多,但是這些應(yīng)該能夠完全覆蓋到線程池的整個(gè)工作流程了。

整個(gè)過(guò)程可以拆分成以下幾個(gè)部分:

提交任務(wù)

當(dāng)向線程池提交一個(gè)新的任務(wù)時(shí),線程池有三種處理情況,分別是:創(chuàng)建一個(gè)工作線程來(lái)執(zhí)行該任務(wù)、將任務(wù)加入阻塞隊(duì)列、拒絕該任務(wù)。

提交任務(wù)的過(guò)程也可以拆分成以下幾個(gè)部分:

當(dāng)工作線程數(shù)小于核心線程數(shù)時(shí),直接創(chuàng)建新的核心工作線程。當(dāng)工作線程數(shù)大于核心線程數(shù)時(shí),就需要嘗試將任務(wù)添加到阻塞隊(duì)列中去。如果能夠加入成功,說(shuō)明隊(duì)列還沒(méi)滿,那么就需要做以下的二次校驗(yàn)來(lái)保證添加進(jìn)去的任務(wù)能夠成功被執(zhí)行。驗(yàn)證當(dāng)前線程池中的運(yùn)行狀態(tài),如果是非RUNNING狀態(tài),則需要將任務(wù)從阻塞隊(duì)列中移除,然后拒絕該任務(wù)。驗(yàn)證當(dāng)前線程池中的工作線程的個(gè)數(shù),如果是0,則需要主動(dòng)添加一個(gè)空工作線程來(lái)執(zhí)行剛剛添加到阻塞隊(duì)列中的任務(wù)。如果加入失敗,說(shuō)明隊(duì)列已經(jīng)滿了,這時(shí)就需要?jiǎng)?chuàng)建新的臨時(shí)工作線程來(lái)執(zhí)行任務(wù)。如果創(chuàng)建成功,則直接執(zhí)行該任務(wù)。如果創(chuàng)建失敗,說(shuō)明工作線程數(shù)已經(jīng)等于最大線程數(shù)了,只能拒絕該任務(wù)了。

整個(gè)過(guò)程可以用下面這張圖來(lái)表示:

一文詳解線程池的工作原理

創(chuàng)建工作線程

創(chuàng)建工作線程需要做一系列的判斷,需要確保當(dāng)前線程池可以創(chuàng)建新的線程之后,才能創(chuàng)建。

首先,當(dāng)線程池的狀態(tài)是SHUTDOWN或者STOP時(shí),不能創(chuàng)建新的線程。其次,當(dāng)線程工廠創(chuàng)建線程失敗時(shí),也不能創(chuàng)建新的線程。第三,拿當(dāng)前工作線程的數(shù)量與核心線程數(shù)、最大線程數(shù)進(jìn)行比較,如果前者大于后者的話,也不允許創(chuàng)建。

除此之外,線程池會(huì)嘗試通過(guò)CAS來(lái)自增工作線程的個(gè)數(shù),如果自增成功了,則會(huì)創(chuàng)建新的工作線程,即Worker對(duì)象。

然后加鎖進(jìn)行二次驗(yàn)證是否能夠創(chuàng)建工作線程,如果最后創(chuàng)建成功,則會(huì)啟動(dòng)該工作線程。

啟動(dòng)工作線程

當(dāng)工作線程創(chuàng)建成功后,也就是Worker對(duì)象已經(jīng)創(chuàng)建好了,這時(shí)就需要啟動(dòng)該工作線程,讓線程開(kāi)始干活了,Worker對(duì)象中關(guān)聯(lián)著一個(gè)Thread,所以要啟動(dòng)工作線程的話,只要通過(guò)worker.thread.start()來(lái)啟動(dòng)該線程即可。

啟動(dòng)完了之后,就會(huì)執(zhí)行Worker對(duì)象的run方法,因?yàn)閃orker實(shí)現(xiàn)了Runnable接口,所以本質(zhì)上Worker也是一個(gè)線程。

通過(guò)線程start開(kāi)啟之后就會(huì)調(diào)用到Runnable的run方法,在Worker對(duì)象的run方法中,調(diào)用了runWorker(this)方法,也就是把當(dāng)前對(duì)象傳遞給了runWorker()方法,讓它來(lái)執(zhí)行。

獲取任務(wù)并執(zhí)行

在runWorker方法被調(diào)用之后,就是執(zhí)行具體的任務(wù)了,首先需要拿到一個(gè)可以執(zhí)行的任務(wù),而Worker對(duì)象中默認(rèn)綁定了一個(gè)任務(wù),如果該任務(wù)不為空的話,那么就是直接執(zhí)行。

執(zhí)行完了之后,就會(huì)去阻塞隊(duì)列中獲取任務(wù)來(lái)執(zhí)行。

獲取任務(wù)的過(guò)程則需要考慮當(dāng)前工作線程的個(gè)數(shù):

如果工作線程數(shù)大于核心線程數(shù),那么就需要通過(guò)poll(keepAliveTime, timeUnit)來(lái)獲取,因?yàn)檫@時(shí)需要對(duì)閑置線程進(jìn)行超時(shí)回收。如果工作線程數(shù)小于等于核心線程數(shù),那么就可以通過(guò)take()來(lái)獲取了。因?yàn)檫@時(shí)所有的線程都是核心線程,不需要進(jìn)行回收,前提是沒(méi)有設(shè)置allowCoreThreadTimeOut(允許核心線程超時(shí)回收)為true。source: www.cnblogs.com/yanggb/p/10629387.html

轉(zhuǎn)載請(qǐng)注明來(lái)自夕逆IT,本文標(biāo)題:《線程池的工作原理(一文詳解線程池的工作原理)》

每一天,每一秒,你所做的決定都會(huì)改變你的人生!

發(fā)表評(píng)論

快捷回復(fù):

評(píng)論列表 (暫無(wú)評(píng)論,21人圍觀)參與討論

還沒(méi)有評(píng)論,來(lái)說(shuō)兩句吧...

丁香五月玖玖爱俺| 无码中文字幕亚洲一区二区三区| 男女十八禁蜜桃一区| 123ww毛片| 超碰人人97最新| 成人无码基地| 婷婷五月中文字幕在线有剧情| 亚洲综合色色色| 亚洲香蕉网在线| 精品一区二区久久久久久久久网站| 51无人区码一码二码三码区别| 神马国产麻豆| 欧美一区精品| 午夜福利试看120秒体验区| 久久高清无码视频| 色噜噜精品| 国产乱人伦APP精品久久| 国产69精品久久久久久妇女 | 精品久线观看视频国产| 色哟哟国产精品专区| 日本高清国产精品| 久久久99婷婷综合精品| 最新法国中文字幕一区| 刺激国产插插视频| 国产网曝网址| 中文人妻无码一区二区三区在线| 无码人妻一区二区三区手| 成人精品国产亚洲欧洲| 日韩在线毛片| 超级AV碰碰| 午夜理论无码片在线观看免费| 先锋激情网| wwwu成人免费| 产精品无码久久_亚洲国产精| 久久A级国产毛片| 欧美熟妇一,二区| 欧美精品一级在线播放| 无码人妻aⅤ区| 亚洲特级毛片无码勉费观看| 亚洲区另类春色综合小说 | 日本国产欧美亚洲精品视|