Java線程基礎(chǔ)

創(chuàng)建線程的三種方式:

  • ? 繼承Thread

  • ? 實(shí)現(xiàn)Runnable接口,然后交給Thread執(zhí)行

  • ? 實(shí)現(xiàn)Callable接口,通過FutureTask,可以拿到回調(diào)值

    嚴(yán)格來說只有前兩種(如Thread源碼中注釋所說)第三種其實(shí)是被包裝成FutureTask交給Thread執(zhí)行,而FutureTask實(shí)現(xiàn)RunnableFuture,RunnableFuture繼承自Runnable)

線程相關(guān)的一些方法

  • sleep期間不會(huì)釋放鎖,結(jié)束后會(huì)進(jìn)入就緒狀態(tài),調(diào)用sleep時(shí)如果interrupted為true,則會(huì)拋出InterruptedException異常,并清除interrupt狀態(tài)為false,想要徹底打斷,需要在捕獲異常時(shí)再調(diào)用interrupt()

  • wait會(huì)釋放鎖,需要等待別人喚醒才能進(jìn)入就緒狀態(tài)

  • interrupt和諧的結(jié)束線程,設(shè)置標(biāo)識(shí)

  • join可以實(shí)現(xiàn)順序執(zhí)行線程,直接獲取當(dāng)前線程的執(zhí)行權(quán)執(zhí)行自己,自己執(zhí)行完后再執(zhí)行當(dāng)前線程

  • setDaemon(),為當(dāng)前線程設(shè)置守護(hù)線程,當(dāng)前線程結(jié)束,守護(hù)線程也會(huì)結(jié)束

線程的一些狀態(tài)

image.png

注:**在顯式鎖調(diào)用lock()獲取不到鎖時(shí)會(huì)調(diào)用LockSupport.park()進(jìn)入等待或者等待超時(shí)狀態(tài),不會(huì)進(jìn)入阻塞態(tài),只有使用synchronized關(guān)鍵字獲取不到鎖時(shí)才會(huì)進(jìn)入阻塞態(tài),等待狀態(tài)是自己調(diào)用方法主動(dòng)進(jìn)入,而阻塞狀態(tài)是被動(dòng)進(jìn)入

ThreadLocal理解

  • 每個(gè)Thread中都持有一個(gè)ThreadLocalMap對(duì)象,這個(gè)對(duì)象存儲(chǔ)了一組以ThreadLocal對(duì)象為鍵,以本地線程變量為value的鍵值對(duì),叫做Entry的數(shù)組。可以在不同的線程中通過同一個(gè)ThreadLocal的set方法設(shè)置不同的線程局部變量,鍵都是同一個(gè)ThreadLocal對(duì)象,但是值保存在不同的Thread的ThreadLocalMap里,這樣再通過同一個(gè)ThreadLocal的get方法取的時(shí)候,同一個(gè)key,在不同的線程卻從不同的ThreadLocalMap取,值也是各自線程之前保存的局部變量值,所以ThreadLocal能夠?qū)崿F(xiàn)線程間的“數(shù)據(jù)隔離”,獲取當(dāng)前線程的局部變量值,不受其他線程影響~
  • 每個(gè)Thread維護(hù)著一個(gè)ThreadLocalMap的引用
  • ThreadLocalMap是ThreadLocal的內(nèi)部類,用Entry來進(jìn)行存儲(chǔ)
  • 調(diào)用ThreadLocal的set()方法時(shí),實(shí)際上就是往自己線程Thread對(duì)象的ThreadLocalMap設(shè)置值,key是ThreadLocal對(duì)象,值是傳遞進(jìn)來的對(duì)象
  • 調(diào)用ThreadLocal的get()方法時(shí),實(shí)際上就是從各自的Thread對(duì)象的ThreadLocalMap獲取值,key是ThreadLocal對(duì)象
  • ThreadLocal本身并不存儲(chǔ)值,它只是作為一個(gè)key來讓線程Thread對(duì)象從各自的ThreadLocalMap獲取value。

線程池

  • 線程池的優(yōu)勢(shì):

    • 降低資源消耗,通過重復(fù)利用自己創(chuàng)建的線程降低線程的創(chuàng)建和銷毀造成的消耗
    • 提高響應(yīng)速度,當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要等到線程創(chuàng)建就可以立即執(zhí)行
    • 提高線程的可管理性,線程是稀缺資源,無限的創(chuàng)建,不僅消耗資源,還會(huì)降低系統(tǒng)穩(wěn)定性,線程池可以統(tǒng)一分配,調(diào)優(yōu)和監(jiān)控
  • 線程池參數(shù)

    • 核心線程數(shù)

    • 最大線程數(shù)

    • 線程空閑時(shí)存活

    • 存活時(shí)間單位

    • 阻塞隊(duì)列

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

    • 4種拒絕策略:1.默認(rèn)策略直接拋出異常。2.拋棄這個(gè)任務(wù)。3.拋棄阻塞隊(duì)列最靠前的。4.用調(diào)用者所在線程執(zhí)行任務(wù)

  • 線程工作機(jī)制:

    • 如果當(dāng)前運(yùn)行的線程少于核心線程數(shù),則創(chuàng)建新線程來執(zhí)行任務(wù)(注意,執(zhí)行這一步驟需要獲取全局鎖)。
    • 如果運(yùn)行的線程等于或多于核心線程數(shù),則將任務(wù)加入阻塞隊(duì)列BlockingQueue。
    • 如果無法將任務(wù)加入BlockingQueue(隊(duì)列已滿),則創(chuàng)建新的線程來處理任務(wù)。
    • 如果創(chuàng)建新線程將使當(dāng)前運(yùn)行的線程超出maximumPoolSize,任務(wù)將被拒絕,并調(diào)用RejectedExecutionHandler.rejectedExecution()方法。
  • 如何配置線程池

    • CPU密集型:CPU在不停的計(jì)算,從內(nèi)存中取值純計(jì)算。

      盡量使用較小的線程池,最大線程數(shù)一般為CPU核心數(shù)+1(由于內(nèi)存比較緊張,會(huì)把一部分磁盤劃分為虛擬內(nèi)存,線程處理的數(shù)據(jù)同時(shí)存在虛擬內(nèi)存,磁盤中時(shí),操作系統(tǒng)需要把磁盤中的數(shù)據(jù)調(diào)度到虛擬內(nèi)存中,磁盤中讀取速度比虛擬內(nèi)存中慢,此時(shí)就會(huì)把線程標(biāo)記為頁缺失狀態(tài),會(huì)等數(shù)據(jù)調(diào)度過來后再喚醒線程,這樣會(huì)有空閑CPU),CPU核心數(shù)獲?。篟untime.getRuntime().availableProcessors(), 因?yàn)镃PU密集型任務(wù)使得CPU使用率很高,若開過多的線程數(shù),會(huì)造成CPU過度切換。

    • IO密集型:網(wǎng)絡(luò)通訊、讀寫磁盤

      可以使用稍大的線程池,一般為2*CPU核心數(shù)。 IO密集型任務(wù)CPU使用率并不高,因此可以讓CPU在等待IO的時(shí)候有其他線程去處理別的任務(wù),充分利用CPU時(shí)間。

    • 混合型:以上兩種都有,這種情況下,可以將任務(wù)分成IO密集型和CPU密集型任務(wù),然后分別用不同的線程池去處理。 如果拆分之后兩個(gè)任務(wù)執(zhí)行時(shí)間有數(shù)據(jù)級(jí)的差距,那么拆分沒有意義。
      因?yàn)橄葓?zhí)行完的任務(wù)就要等后執(zhí)行完的任務(wù),最終的時(shí)間仍然取決于后執(zhí)行完的任務(wù),而且還要加上任務(wù)拆分與合并的開銷,如果拆分之后兩個(gè)任務(wù)執(zhí)行時(shí)間差不多,那拆分效率更高

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容