創(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)

注:**在顯式鎖調(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í)間差不多,那拆分效率更高
-