進(jìn)程與線程
1.進(jìn)程:
? ? ? ?進(jìn)程是資源分配的基本單位启妹,又是調(diào)度運(yùn)行的基本單位,是系統(tǒng)中并發(fā)執(zhí)行的單位杖爽。進(jìn)程是一個(gè)具有一定獨(dú)立功能的程序關(guān)于某個(gè)數(shù)據(jù)集合的一次運(yùn)行活動(dòng)诚欠。
① 進(jìn)程是一個(gè)實(shí)體。每一個(gè)進(jìn)程都有它自己的地址空間草丧,一般情況下狸臣,包括代碼段、數(shù)據(jù)區(qū)和堆棧昌执;
② 進(jìn)程是一個(gè)“執(zhí)行中的程序”固棚。程序是一個(gè)沒(méi)有生命的實(shí)體,只有處理器賦予程序生命時(shí)仙蚜,它才能成為一個(gè)活動(dòng)的實(shí)體,我們稱其為進(jìn)程厂汗。
2.線程:
? ? ? ?線程是進(jìn)程中執(zhí)行運(yùn)算的最小單位委粉,通常在一個(gè)進(jìn)程中可以包含若干個(gè)線程,這若干個(gè)線程可以被同時(shí)調(diào)度到多個(gè)CPU上運(yùn)行娶桦,線程可以利用進(jìn)程所擁有的資源贾节。
? ? ? ?多線程是為了同步完成多項(xiàng)任務(wù)汁汗,不是為了提高運(yùn)行效率,而是為了提高資源使用效率來(lái)提高系統(tǒng)的效率栗涂。線程是在同一時(shí)間需要完成多項(xiàng)任務(wù)的時(shí)候?qū)崿F(xiàn)的知牌。
線程安全問(wèn)題
? ? ? ?在沒(méi)有充足同步的情況下,由于線程內(nèi)存模型及多線程中的操作執(zhí)行順序的不可預(yù)測(cè)性的緣故斤程,對(duì)對(duì)象中的可變狀態(tài)或類的共享狀態(tài)執(zhí)行操作時(shí)角寸,可能會(huì)產(chǎn)生奇怪的結(jié)果。
1.內(nèi)存屏障
2.volatile 修飾符
? ? ? ?保證了共享變量的可見(jiàn)性忿墅,當(dāng)一個(gè)線程修改一個(gè)共享變量時(shí)扁藕,另外一個(gè)線程能夠讀到被修改的值,不保證其原子性疚脐。
? ? ? ?在對(duì)volatile 變量進(jìn)行寫操作時(shí)會(huì)多出lock匯編代碼亿柑,該指令會(huì)引發(fā)兩件事情:
1.lock前綴指令會(huì)引起處理器將緩存行寫回到內(nèi)存,以前的處理器通過(guò)LOCK#信號(hào)鎖總線棍弄,而目前的處理器則是通過(guò)緩存鎖定望薄,鎖定這塊內(nèi)存區(qū)域的緩存;
2.上述寫回操作會(huì)使其他CPU里緩存了該內(nèi)存地址的數(shù)據(jù)無(wú)效呼畸;通過(guò)緩存一致性協(xié)議痕支,每個(gè)處理器通過(guò)嗅探在總線上傳播的數(shù)據(jù)(對(duì)共享數(shù)據(jù)的修改)來(lái)查看自己緩存的值是否過(guò)期。
可見(jiàn)性:對(duì)一個(gè)volatile變量的讀役耕,總是能看到(任意線程)對(duì)這個(gè)volatile變量最后的寫入采转;
原子性:對(duì)任意volatile變量的單個(gè)讀/寫操作具有原子性,但類似volatile++這種復(fù)合操作不具有原子性瞬痘;
volatile寫-讀的內(nèi)存語(yǔ)義
? ? ? ?當(dāng)寫一個(gè)volatile變量時(shí)故慈,JMM會(huì)把該線程的本地內(nèi)存中的共享變量刷新到主內(nèi)存;
? ? ? ?當(dāng)讀一個(gè)volatile變量時(shí)框全,JMM會(huì)直接從主內(nèi)存中讀取共享變量察绷;
volatile寫-讀的內(nèi)存語(yǔ)義實(shí)現(xiàn)
StoreStore屏障-volatile寫-StoreLoad屏障;
volatile讀-LoadLoad屏障-LoadStore屏障津辩;
? ? ? ?只要volatile變量和普通變量之間的重排序可能會(huì)破壞volatile的內(nèi)存語(yǔ)義拆撼,這種重排序就會(huì)被編譯器重排序規(guī)則和處理器內(nèi)存屏蔽插入策略禁止;
3.synchronized
? ? ? ?靜態(tài)同步方法鎖住Class對(duì)象喘沿,普通同步方法鎖住當(dāng)前對(duì)象闸度,同步代碼塊鎖住配置的對(duì)象。JVM基于進(jìn)入和退出Monitor對(duì)象來(lái)實(shí)現(xiàn)同步蚜印。同步代碼塊→monitorenter莺禁,monitorexit;同步方法→ACC_SYNCHRONIZED窄赋。任意線程對(duì)Object的同步訪問(wèn)哟冬,首先要獲得Object的監(jiān)視器楼熄,如果獲取失敗,線程進(jìn)入同步隊(duì)列浩峡,線程狀態(tài)變?yōu)锽locked可岂。當(dāng)訪問(wèn)Object的前驅(qū)釋放了鎖,則該釋放操作會(huì)喚醒阻塞隊(duì)列中的線程翰灾,使其重新嘗試對(duì)監(jiān)視器的獲取缕粹。
? ? ? ?釋放鎖和獲取鎖與volatile寫-讀具有相同的內(nèi)存語(yǔ)義,在多處理器上進(jìn)行CAS時(shí)预侯,會(huì)在cmpxchg加上lock前綴蒸殿。
? ? ? ?lock前綴:確保對(duì)內(nèi)存的讀-改-寫操作原子執(zhí)行脱拼;禁止該指令與之前和之后的讀寫指令重排序回还;把寫緩存區(qū)中的所有數(shù)據(jù)刷新到內(nèi)存中抛人。
4.JAVA對(duì)象頭
? ? ? ?如果對(duì)象是數(shù)組,則對(duì)象頭尾3個(gè)字寬糜芳,否則為2個(gè)字寬飒货;JAVA對(duì)象頭里的MarkWord里默認(rèn)存儲(chǔ)對(duì)象的HashCode、分代年齡和鎖標(biāo)記位峭竣。
5.鎖的升級(jí)和對(duì)比
鎖一共有4種狀態(tài)塘辅,由低到高分別為:無(wú)鎖、偏向鎖皆撩、輕量級(jí)鎖扣墩、重量級(jí)鎖,鎖狀態(tài)會(huì)隨著競(jìng)爭(zhēng)情況逐漸升級(jí)扛吞,鎖可以升級(jí)但不能降級(jí)呻惕;
偏向鎖
? ? ? ?當(dāng)一個(gè)線程訪問(wèn)同步代碼塊并獲取鎖時(shí),會(huì)在對(duì)象頭和棧幀的鎖記錄里面存儲(chǔ)鎖偏向的線程ID滥比,以后該線程在進(jìn)入和退出同步塊時(shí)不需要進(jìn)行CAS操作來(lái)加鎖和釋放鎖(如果鎖仍然偏向該線程亚脆,若不則CAS操作將對(duì)象頭的偏向鎖指向該線程),只需簡(jiǎn)單測(cè)試對(duì)象頭的偏向鎖記錄是否仍然指向該線程盲泛。
? ? ? ?偏向鎖的撤銷濒持,出現(xiàn)競(jìng)爭(zhēng)才釋放鎖,撤銷需要等待全局安全點(diǎn)寺滚,暫停并檢查用于偏向鎖的線程柑营,若不處于活動(dòng)狀態(tài)則置為無(wú)鎖狀態(tài),若線程仍活著則暫停并恢復(fù)對(duì)象頭為無(wú)鎖或標(biāo)記對(duì)象不適合作為偏向鎖村视,然后喚醒線程官套;
輕量級(jí)鎖
加鎖
在當(dāng)前線程的棧幀中創(chuàng)建存儲(chǔ)鎖記錄的空間,并復(fù)制對(duì)象頭中的Mark Word到鎖記錄中(Displaced Mark Word),然后使用CAS將對(duì)象頭中的Mark Word替換為指向鎖記錄的指針虏杰,成功則獲得鎖,失敗則表示有其他線程競(jìng)爭(zhēng)鎖勒虾,嘗試自旋來(lái)獲取鎖纺阔;-
解鎖,通過(guò)CAS操作將Displaced Mark Word替換回對(duì)象頭修然,成功(a &b)則表示沒(méi)有競(jìng)爭(zhēng)笛钝;失敗則鎖膨脹成了重量級(jí)鎖,喚醒等待的線程愕宋,但都會(huì)釋放鎖玻靡;
a. 對(duì)象頭中的Mark Word中的鎖記錄是否仍然指向當(dāng)前線程鎖記錄;
b. 拷貝在當(dāng)前線程鎖記錄的Mark Word信息是否與對(duì)象頭中的Mark Word一致中贝;
鎖原理圖
6.原子操作的實(shí)現(xiàn)
1) 處理器如何實(shí)現(xiàn)原子操作囤捻?
- 總線鎖(開(kāi)銷大),使用處理器提供的一個(gè)LOCK#信號(hào)邻寿,當(dāng)一個(gè)處理器在總線上輸出此信號(hào)時(shí)蝎土,其他處理器的請(qǐng)求將被阻塞,其與內(nèi)存的通信被鎖住绣否,那么該處理器可以獨(dú)享共享內(nèi)存
- 緩存鎖誊涯,保證同一時(shí)刻對(duì)某個(gè)內(nèi)存地址的操作是原子的,內(nèi)存區(qū)域如果被緩存在處理器的緩存行中蒜撮,并且在LOCK操作期間被鎖定暴构,那么當(dāng)將緩存寫回內(nèi)存時(shí),通過(guò)修改內(nèi)部的內(nèi)存地址并允許它的緩存一致性來(lái)保證操作的原子性段磨。但是當(dāng)操作的數(shù)據(jù)不能被緩存或操作的數(shù)據(jù)跨越多個(gè)緩存行時(shí)處理器會(huì)調(diào)用總線鎖定取逾,還有部分處理器不支持緩存鎖定;
2) JAVA如何實(shí)現(xiàn)原子操作薇溃?
- 循環(huán)CAS操作菌赖,利用處理器提供的CMPXCHG指令實(shí)現(xiàn);
CAS實(shí)現(xiàn)原子操作的三大問(wèn)題:
1.ABA問(wèn)題沐序,一個(gè)值從A→B→A變化琉用,使用CAS檢查會(huì)發(fā)現(xiàn)值無(wú)變化,解決辦法是變量前面追加版本號(hào)策幼;
2.循環(huán)時(shí)間長(zhǎng)開(kāi)銷大邑时,自旋CAS如果長(zhǎng)時(shí)間不成功會(huì)給CPU帶來(lái)非常大的執(zhí)行開(kāi)銷;
3.只能保證一個(gè)共享變量的原子操作特姐,將多變量合并晶丘,JDK1.5提供的AtomicReference 類來(lái)保證引用對(duì)象的原子性,可以將多個(gè)變量封裝到一個(gè)對(duì)象中來(lái)進(jìn)行CAS;
- 使用鎖機(jī)制來(lái)實(shí)現(xiàn)原子操作浅浮,保證了只有獲得鎖的線程才能夠操作鎖定的內(nèi)存區(qū)域沫浆,除了偏向鎖,JVM實(shí)現(xiàn)鎖的方式都采用了循環(huán)CAS滚秩,在進(jìn)入時(shí)通過(guò)循環(huán)CAS獲取鎖专执,在退出同步塊時(shí)使用循環(huán)CAS來(lái)釋放鎖;
7.從源代碼到指令序列的重排序
? ? ? ?1)編譯器優(yōu)化重排序郁油,在不改變單線程程序語(yǔ)義的前提下本股,可以重新安排語(yǔ)句的執(zhí)行順序;
? ? ? ?2)指令級(jí)并行重排序桐腌,現(xiàn)代處理器采用指令級(jí)并行技術(shù)來(lái)將多條指令重疊執(zhí)行拄显,如果不存在數(shù)據(jù)依賴,處理器可以改變語(yǔ)句對(duì)應(yīng)機(jī)器指令的執(zhí)行順序案站;
? ? ? ?3)內(nèi)存系統(tǒng)重排序躬审,由于處理器使用緩存和讀/寫緩沖區(qū),使得加載和存儲(chǔ)操作看上去是亂序執(zhí)行的嚼吞;
? ? ? ?上述1)屬于編譯器重排序盒件,2)3)屬于處理器重排序,可能會(huì)導(dǎo)致內(nèi)存可見(jiàn)性問(wèn)題舱禽。對(duì)于1)JMM的編譯器重排序規(guī)則會(huì)禁止特定類型的重排序炒刁,
? ? ? ?對(duì)于2)和3),JMM的處理器重排序規(guī)則會(huì)要求JAVA編譯器在生成指令序列時(shí)誊稚,插入特定類型的內(nèi)存屏障指令翔始,通過(guò)內(nèi)存屏障指令來(lái)禁止特定類型的重排序;
? ? ? ?數(shù)據(jù)依賴性:如果兩個(gè)操作訪問(wèn)同一個(gè)變量里伯,且這兩個(gè)操作有一個(gè)為寫操作城瞎,此時(shí)這2個(gè)操作之間存在數(shù)據(jù)依賴性,在重排序時(shí)疾瓮,不會(huì)改變存在數(shù)據(jù)依賴關(guān)系的2個(gè)操作的執(zhí)行順序脖镀;
8.順序一致性模型&JAVA內(nèi)存模型
順序一致特性:一個(gè)線程中的所有操作必須按照程序的順序來(lái)執(zhí)行;(不管程序是否同步)所有線程都只能看到一個(gè)單一的操作執(zhí)行順序狼电。
未同步程序的執(zhí)行特性:JMM只提供最小安全性蜒灰,線程執(zhí)行時(shí)讀取的值要么是之前某個(gè)線程寫入的值,要么是默認(rèn)值(0,NULL,FALSE)肩碟。為了實(shí)現(xiàn)最小安全性强窖,JVM在堆上分配對(duì)象時(shí),首先會(huì)對(duì)內(nèi)存空間清零削祈,然后才會(huì)在上面分配對(duì)象翅溺,在已清理的內(nèi)存空間分配對(duì)象時(shí)脑漫,域的默認(rèn)初始化已經(jīng)完成。
未同步程序在兩個(gè)模型中的差異:
1)順序一致性模型保證單線程內(nèi)的操作會(huì)按程序順序執(zhí)行咙崎,而JMM不保證(臨界區(qū)代碼重排序)优幸;
2)順序一致性模型保證所有線程只能看到一致的操作執(zhí)行順序,而JMM不保證(臨界區(qū)代碼重排序)褪猛;
3)JMM不保證對(duì)64位的Long和Double變量的寫操作是原子的劈伴,而順序一致性模型保證對(duì)所有的內(nèi)存讀寫操作都具有原子性;
9.final域
final域的重排序規(guī)則
a)在構(gòu)造函數(shù)內(nèi)對(duì)一個(gè)final域的寫入握爷,與隨后把這個(gè)被構(gòu)造對(duì)象的引用賦值給一個(gè)引用變量,這兩個(gè)操作之間不能重排序(保證在對(duì)象引用為任意線程可見(jiàn)之前严里,對(duì)象的final域已經(jīng)被正確的初始化新啼,保證在構(gòu)造函數(shù)內(nèi)部,不能讓這個(gè)被構(gòu)造對(duì)象的引用為其他線程所見(jiàn))刹碾,在final寫之后燥撞,構(gòu)造函數(shù)返回之前插入StoreStore內(nèi)存屏障;
b)初次讀一個(gè)包含final域的對(duì)象的引用迷帜,與隨后初次讀該對(duì)象的final域物舒,這兩個(gè)操作之間不能重排序(保證在讀一個(gè)對(duì)象的引用之前,一定會(huì)先讀包含這個(gè)final域的對(duì)象的引用)戏锹,在final讀之前插入LoadLoad內(nèi)存屏障冠胯;
? ? ? ?X86處理器不會(huì)對(duì)寫-寫和間接依賴關(guān)系的操作進(jìn)行重排序,所以在X86處理器中final域的讀/寫不會(huì)插入任何內(nèi)存屏障锦针。
? ? ? ?JSR-133中荠察,只要對(duì)象正確構(gòu)造(對(duì)象引用不會(huì)由構(gòu)造器溢出),那么不需要同步就可以保證任意線程都能看到final域在構(gòu)造函數(shù)中被初始化之后的值奈搜。
10.雙重檢查鎖定與延遲初始化
延遲初始化:推遲高開(kāi)銷的對(duì)象初始化操作悉盆,在使用對(duì)象時(shí)才進(jìn)行初始化。
雙重檢查鎖定:直接對(duì)getInstance()加鎖存在具體的開(kāi)銷馋吗。
if(instance == null){//1
synchronized(Main.class){//2
if(instance == null){//3
instance = new Main()//4
}
}
}
存在的問(wèn)題
? ? ? ?第4行焕盟,代碼讀到instance不為null時(shí),對(duì)象可能還沒(méi)有完成初始化宏粤;創(chuàng)建一個(gè)對(duì)象可以分為三步:a.分配對(duì)象內(nèi)存空間脚翘,b.初始化對(duì)象,c.將對(duì)象引用指向剛分配的內(nèi)存地址商架,由于在單線程中b堰怨,c重排序不會(huì)改變執(zhí)行結(jié)果,但在多線程并發(fā)情況下蛇摸,b备图,c的重排序可能導(dǎo)致線程在創(chuàng)建對(duì)象時(shí)先執(zhí)行c而導(dǎo)致其他線程判斷instance不為空,但此時(shí)對(duì)象初始化并未完成。
解決方案:
1) 將引用對(duì)象instance聲明為volatile揽涮,實(shí)際為禁止b抠藕,c的重排序;
2) 基于類初始化
在發(fā)生下列任意一種情況時(shí)蒋困,一個(gè)類/接口類型T將被立即初始化:
T是一個(gè)類盾似,而且一個(gè)T類型的實(shí)例被創(chuàng)建(new)
T是一個(gè)類,且T中聲明的靜態(tài)方法被調(diào)用
T中聲明的一個(gè)靜態(tài)字段被賦值
T中聲明的一個(gè)靜態(tài)字段被調(diào)用雪标,且該字段不是一個(gè)常量字段
T是一個(gè)頂級(jí)類零院,且一個(gè)斷言語(yǔ)句嵌套在T內(nèi)部被執(zhí)行
JVM在類初始化期間會(huì)獲取與該類對(duì)應(yīng)的初始化鎖,并且每個(gè)線程至少獲取一次鎖來(lái)確保該類已被初始化村刨。
類初始化過(guò)程中的同步處理:
第一階段:通過(guò)在Class對(duì)象上同步(獲取Class對(duì)象初始化鎖)告抄,來(lái)控制類或接口的初始化。獲取鎖線程會(huì)一直阻塞直到獲取到該初始化鎖嵌牺。
第二階段:線程A執(zhí)行類的初始化打洼,同時(shí)線程B在初始化鎖對(duì)應(yīng)的condition上等待;
第三階段:線程A設(shè)置state=initialized逆粹,喚醒在condition中等待的所有線程募疮;
第四階段:線程B結(jié)束類的初始化處理;
第五階段:線程C執(zhí)行類的初始化的處理僻弹,過(guò)程同上表(不會(huì)在condition中等待阿浓,只經(jīng)歷一次鎖的獲取和釋放);
11.線程
線程是操作系統(tǒng)調(diào)度的最小單位蹋绽;
1.為什么要使用多線程搔扁?
- 更多的處理器核心,由于一個(gè)線程在同一時(shí)刻只能運(yùn)行在一個(gè)處理器核心上蟋字,隨著處理器上的核心數(shù)量越來(lái)越多稿蹲,使用多線程能夠同時(shí)使用多個(gè)處理器核心,顯著減少程序處理時(shí)間鹊奖。
- 更快的響應(yīng)時(shí)間苛聘,使用多線程技術(shù)將數(shù)據(jù)一致性不強(qiáng)的操作派發(fā)給其他線程處理,最大化利用CPU時(shí)間片忠聚。
- 更好的編程模型
2.線程的狀態(tài)
3.Daemon線程(守護(hù)線程)
? ? ? ?當(dāng)一個(gè)JAVA虛擬機(jī)中不存在非Daemon線程的時(shí)候设哗,虛擬機(jī)將會(huì)退出;通過(guò)Thread.setDaemon(true)進(jìn)行設(shè)置两蟀;Daemon屬性需要在啟動(dòng)線程之前設(shè)置网梢,不能在啟動(dòng)線程之后設(shè)置。當(dāng)所有非Daemon線程結(jié)束赂毯,虛擬機(jī)中的所有Daemon線程需要立即終止战虏,并不執(zhí)行finally塊拣宰。
4.構(gòu)造線程(Thread.init(…)方法)
? ? ? ?線程對(duì)象在構(gòu)造時(shí)需要提供線程所需要的屬性,如線程所屬的線程組烦感、線程優(yōu)先級(jí)巡社、是否時(shí)Daemon線程等;一個(gè)新構(gòu)造的線程對(duì)象是由其parent線程(創(chuàng)建新線程的線程)來(lái)進(jìn)行空間分配的手趣,child線程繼承了parent是否為Daemon晌该、優(yōu)先級(jí)和加載資源的contextClassLoader以及可繼承的ThreadLocal,同時(shí)還會(huì)分配一個(gè)唯一ID來(lái)標(biāo)識(shí)此child線程绿渣。
5.線程中斷
- isInterrupted()判斷是否被中斷朝群,不會(huì)影響中斷標(biāo)志;如果線程處于終止?fàn)顟B(tài)中符,即使線程已經(jīng)被中斷過(guò)潜圃,依舊返回false;
- interrupted()判斷是否被中斷舟茶,并復(fù)位中斷標(biāo)志;
- interrupt()對(duì)線程進(jìn)行中斷堵第;
? ? ? ?在拋出InterruptedException之前吧凉,虛擬機(jī)會(huì)先將該線程的中斷標(biāo)志復(fù)位;
? ? ? ?安全地終止線程:通過(guò)在run()的循環(huán)中加入對(duì)線程中斷標(biāo)志的判斷或者單獨(dú)設(shè)置一個(gè)標(biāo)志踏志,通過(guò)對(duì)該標(biāo)志的置位來(lái)退出循環(huán)阀捅,進(jìn)而退出此線程;
6.過(guò)期的suspend()针余、resume()和stop()饲鄙。suspend()暫停此線程,不釋放占有的資源(比如鎖)圆雁;resume()恢復(fù)此線程忍级;stop()停止此線程,不保證線程資源被正常釋放伪朽;
7.等待/通知機(jī)制(wait/notify)
- notify()通知一個(gè)在對(duì)象上等待的線程轴咱,使其從wait()方法返回,返回前提是該線程獲得了對(duì)象的鎖
- notifyAll()通知所有等待在該對(duì)象上的線程
- wait()調(diào)用該方法的線程進(jìn)入WAITING狀態(tài)烈涮,釋放對(duì)象鎖朴肺,只有等待其他線程的通知或被中斷才返回
- wait(long)超時(shí)等待一段時(shí)間,單位是毫秒坚洽,如果時(shí)間到?jīng)]有通知?jiǎng)t返回
- wait(long,int)對(duì)于超時(shí)時(shí)間的細(xì)粒度控制戈稿,可以達(dá)到納秒
注意:
a 使用wait()、notify()讶舰、notifyAll()時(shí)需要先對(duì)調(diào)用對(duì)象加鎖鞍盗;
b 調(diào)用wait()方法后需了,線程狀態(tài)由RUNNING變?yōu)閃ATING,并將線程放入對(duì)象的等待隊(duì)列橡疼;
c A線程調(diào)用notify()和notifyAll()后援所,等待線程B不會(huì)立即從wait()返回,需要A線程釋放鎖欣除,等待線程B才有機(jī)會(huì)從wait()返回住拭;
d notify()方法將等待隊(duì)列的一個(gè)等待線程從等待隊(duì)列中移到同步隊(duì)列,而notifyAll()則時(shí)將等待隊(duì)列中的所有線程全部移到同步隊(duì)列历帚,被移動(dòng)線程由WATING狀態(tài)變?yōu)锽LOCKED狀態(tài)滔岳;
e 從wait()方法返回的前提時(shí)獲得了調(diào)用對(duì)象的鎖;
12.LOCK與synchronized
LOCK接口提供的synchronized關(guān)鍵字不具備的主要特性
1.嘗試非阻塞地獲取鎖挽牢,當(dāng)前線程獲取鎖時(shí)谱煤,如果此刻鎖沒(méi)有被其他線程獲取,則成功獲取該鎖禽拔;
2.能中斷地獲取鎖刘离,與synchronized不同,獲取鎖的線程能夠響應(yīng)中斷睹栖,當(dāng)獲取到鎖的線程被中斷時(shí)硫惕,會(huì)拋出中斷異常,同時(shí)釋放鎖野来;
3.超時(shí)獲取鎖恼除,在指定的截至?xí)r間之前獲取鎖,如果截至?xí)r間到仍無(wú)法獲取鎖曼氛,則返回豁辉;
13.隊(duì)列同步器(AbstractQueuedSynchronizer)
用于構(gòu)建鎖或其他同步組件的基礎(chǔ)框架,使用一個(gè)int變量表示成員狀態(tài)舀患,通過(guò)內(nèi)置的FIFO隊(duì)列來(lái)完成資源獲取線程的排隊(duì)工作徽级。
1.同步隊(duì)列
? ? ? ?當(dāng)前線程獲取同步狀態(tài)失敗時(shí),同步器會(huì)將當(dāng)前線程及其等待狀態(tài)等信息構(gòu)成一個(gè)NODE并將其加入同步隊(duì)列(雙向鏈表)聊浅,同時(shí)會(huì)阻塞當(dāng)前線程灰追,當(dāng)同步狀態(tài)釋放時(shí),會(huì)把首節(jié)點(diǎn)的線程喚醒狗超,使其再次嘗試獲取同步狀態(tài)弹澎。
2.獨(dú)占式同步狀態(tài)獲取與釋放
? ? ? ?調(diào)用同步器的acquire(int arg)方法獲取同步狀態(tài):通過(guò)調(diào)用自定義的tryacquire()來(lái)線程安全地獲取同步狀態(tài),如果失敗努咐,則構(gòu)造同步節(jié)點(diǎn)并通過(guò)addWaiter(Node node)方法將該節(jié)點(diǎn)加入到同步隊(duì)列的尾部苦蒿,最后調(diào)用acquireQueue(),使得該節(jié)點(diǎn)以“死循環(huán)“的方式獲取同步狀態(tài)渗稍,如果獲取不到則阻塞節(jié)點(diǎn)中的線程佩迟,被阻塞線程的喚醒主要依靠前驅(qū)節(jié)點(diǎn)的出隊(duì)或阻塞線程被中斷來(lái)實(shí)現(xiàn)团滥,只有前驅(qū)節(jié)點(diǎn)是頭節(jié)點(diǎn)才能嘗試獲取同步狀態(tài),這是為什么报强?
a.頭節(jié)點(diǎn)是成功獲取到同步狀態(tài)的節(jié)點(diǎn)灸姊,而頭節(jié)點(diǎn)的線程釋放了同步狀態(tài)之后,將會(huì)喚醒其后繼節(jié)點(diǎn)秉溉,后繼節(jié)點(diǎn)的線程被喚醒后需要檢查自己的前驅(qū)是否是頭節(jié)點(diǎn)力惯;
b.維護(hù)同步隊(duì)列的FIFO原則;
? ? ? ?在釋放同步狀態(tài)時(shí)召嘶,同步器調(diào)用tryRelease()方法釋放同步狀態(tài)父晶,并喚醒頭節(jié)點(diǎn)的后繼節(jié)點(diǎn);
3.共享式同步狀態(tài)獲取與釋放
? ? ? ?通過(guò)調(diào)用tryAcquireShared()方法嘗試獲得同步狀態(tài)弄跌,返回值大于等于0甲喝,表示能夠獲取到同步狀態(tài),若失敗則進(jìn)入自旋過(guò)程doAcquireShared()铛只,當(dāng)前驅(qū)是否為頭節(jié)點(diǎn)時(shí)嘗試獲取同步狀態(tài)埠胖,如果返回值大于等于0,則成功并從自旋過(guò)程中退出淳玩。
? ? ? ?釋放過(guò)程與獨(dú)占式的區(qū)別主要在于tryReleaseShared()方法必須通過(guò)循環(huán)和CAS確保同步狀態(tài)線程安全釋放
4.獨(dú)占式超時(shí)獲取同步狀態(tài)
? ? ? ?通過(guò)自旋直撤,當(dāng)前驅(qū)節(jié)點(diǎn)為頭節(jié)點(diǎn)時(shí)嘗試獲取同步狀態(tài),失敗則判斷是否超時(shí)凯肋,沒(méi)有則重新設(shè)置超時(shí)時(shí)間,然后讓當(dāng)前線程等待汽馋,超時(shí)則直接返回侮东;
? ? ? ?當(dāng)nanosTimeout小于等于spinForTimeoutThreshold(1000納秒),則不會(huì)使該線程進(jìn)行超時(shí)等待豹芯,而是進(jìn)入快速自旋過(guò)程悄雅;
14.重入鎖
? ? ? ?表示該鎖能夠支持一個(gè)線程對(duì)資源的重復(fù)加鎖,該鎖還支持獲取鎖時(shí)的公平和非公平性選擇铁蹈,如果在絕對(duì)時(shí)間上宽闲,先對(duì)鎖進(jìn)行獲取的請(qǐng)求一定先被滿足,則這個(gè)鎖是公平的握牧,反之容诬,是不公平的。
? ? ? ?ReentrantLock通過(guò)判斷當(dāng)前線程是否為獲取鎖的線程來(lái)決定操作是否成功沿腰,成功獲取鎖的線程再次獲取鎖只是增加了同步狀態(tài)值览徒,在釋放同步狀態(tài)時(shí)減少相應(yīng)的值。
15.公平鎖與非公平鎖
? ? ? ?公平性與否時(shí)針對(duì)獲取鎖而言颂龙,如果一個(gè)鎖是公平的习蓬,那么鎖的獲取順序應(yīng)該符合請(qǐng)求的絕對(duì)順序纽什,也就是FIFO。
? ? ? ?非公平鎖躲叼,只要CAS設(shè)置同步狀態(tài)成功芦缰,則表示當(dāng)前線程獲取了鎖,剛釋放鎖的線程重新獲得同步狀態(tài)的幾率非常大枫慷。雖然可能造成線程“饑餓”让蕾,但極少的線程切換,保證了更大的吞吐量
? ? ? ?公平鎖流礁,在非公平鎖的基礎(chǔ)上涕俗,加入了對(duì)前驅(qū)節(jié)點(diǎn)的判斷,只有等前驅(qū)節(jié)點(diǎn)獲取并釋放了鎖之后才能獲取鎖神帅。保證了鎖的獲取按照FIFO原則再姑,代價(jià)是大量的線程切換。
16.讀寫鎖(ReentrantReadWriteLock)
? ? ? ?讀寫鎖在同一時(shí)刻允許多個(gè)讀線程訪問(wèn)找御,但在寫線程訪問(wèn)時(shí)元镀,所有其他線程都被阻塞;ReentrantReadWriteLock通過(guò)一個(gè)整形變量的高16位維護(hù)讀狀態(tài)霎桅,低16位維護(hù)寫狀態(tài)栖疑。
1.寫鎖的獲取與釋放
? ? ? ?獲取寫鎖時(shí),在重入條件之上增加了對(duì)讀鎖是否存在的判斷滔驶;釋放寫鎖時(shí)遇革,每次釋放減少寫狀態(tài)(低16位),當(dāng)寫狀態(tài)為0表示寫鎖已經(jīng)被成功釋放揭糕,并置獲取鎖線程為null萝快。
2.讀鎖的獲取與釋放
? ? ? ?獲取讀鎖時(shí),如果其他線程已經(jīng)獲取寫鎖著角,則當(dāng)前線程獲取讀鎖失敗揪漩,進(jìn)入等待狀態(tài);如果當(dāng)前線程獲取了寫鎖或者寫鎖未被獲取吏口,則當(dāng)前線程成功獲取讀鎖奄容;先快速獲取,如果失敗則“死循環(huán)”獲取讀鎖产徊。
? ? ? ?釋放讀鎖時(shí)昂勒,讀狀態(tài)減少1<<16;
3.鎖降級(jí)
? ? ? ?鎖降級(jí)指寫鎖降級(jí)成為讀鎖舟铜,擁有寫鎖叁怪,再獲取讀鎖,隨后釋放當(dāng)前擁有的寫鎖深滚。中間獲取讀鎖是為了保證數(shù)據(jù)的可見(jiàn)性奕谭,如果當(dāng)前線程A不獲取讀鎖直接釋放寫鎖涣觉,假設(shè)此刻有另一個(gè)線程B獲取了寫鎖并修改了數(shù)據(jù),那么當(dāng)前線程A無(wú)法感知線程B對(duì)數(shù)據(jù)的更新血柳。同理不支持鎖升級(jí)官册。
17.Condition
? ? ? ?每個(gè)Condition對(duì)象都包含著一個(gè)隊(duì)列(等待隊(duì)列),用于實(shí)現(xiàn)等待/通知功能难捌。
1.等待隊(duì)列
? ? ? ?等待隊(duì)列是一個(gè)FIFO隊(duì)列膝宁,隊(duì)列中每個(gè)節(jié)點(diǎn)包含一個(gè)線程引用,該線程就是在Condition上等待的線程根吁,如果一個(gè)線程調(diào)用了Condition.await()方法员淫,該線程會(huì)釋放鎖、構(gòu)造成節(jié)點(diǎn)并加入等待隊(duì)列進(jìn)入等待狀態(tài)击敌。
? ? ? ?在Object的監(jiān)視器模型上介返,一個(gè)對(duì)象包含一個(gè)同步隊(duì)列和等待隊(duì)列;而并發(fā)包中的Lock擁有一個(gè)同步隊(duì)列和多個(gè)等待隊(duì)列沃斤。
2.等待
? ? ? ?調(diào)用Condition的await()方法圣蝎,使當(dāng)前線程進(jìn)入等待隊(duì)列并釋放鎖,同時(shí)線程狀態(tài)變?yōu)榈却隣顟B(tài)衡瓶。相當(dāng)于同步隊(duì)列的首節(jié)點(diǎn)移動(dòng)到了等待隊(duì)列的尾節(jié)點(diǎn)徘公。
3.通知
? ? ? ?調(diào)用Condition的signal()方法,若當(dāng)前線程獲取了鎖哮针,將會(huì)喚醒在等待隊(duì)列中等待時(shí)間最長(zhǎng)的節(jié)點(diǎn)(首節(jié)點(diǎn))关面,在喚醒節(jié)點(diǎn)前會(huì)將節(jié)點(diǎn)移到同步隊(duì)列(隊(duì)尾),最后通過(guò)LockSupport喚醒節(jié)點(diǎn)中的線程
18.ConcurrentHashMap
? ? ? ?不支持key或value為null十厢;hash值通過(guò)key的hash值與其無(wú)符號(hào)右移16位的值異或并除去符號(hào)位得到等太。
1.為什么要使用ConcurrentHashMap
? ? ? ?在多線程環(huán)境下袒哥,使用HashMap進(jìn)行put操作可能會(huì)引起死循環(huán),因?yàn)槎嗑€程會(huì)導(dǎo)致HashMap的Entry鏈表形成環(huán)形數(shù)據(jù)結(jié)構(gòu)扑毡;
? ? ? ?Hashtable使用synchronized來(lái)保證線程安全国夜,效率低下。
2.ConcurrentHashMap如何實(shí)現(xiàn)線程安全性
? ? ? ?在JDK1.7中芒篷,ConcurrentHashMap容器通過(guò)多把鎖,每一把鎖用于鎖容器的其中一部分?jǐn)?shù)據(jù),即分段加鎖徘六,當(dāng)多線程訪問(wèn)容器的不同數(shù)據(jù)段的數(shù)據(jù)時(shí),線程間就不會(huì)存在鎖的競(jìng)爭(zhēng)榴都,從而有效提高了并發(fā)訪問(wèn)效率待锈。
? ? ? ?在JDK1.8中,若桶為空則直接通過(guò)CAS操作添加節(jié)點(diǎn)嘴高,否則synchronized鎖住鏈表的頭節(jié)點(diǎn)或者樹(shù)的根節(jié)點(diǎn)后進(jìn)行添加竿音。
19.并發(fā)工具類
1.CountDownLatch
? ? ? ?運(yùn)行一個(gè)或多個(gè)線程等待其他線程完成操作接受一個(gè)int類型參數(shù)N(N>=0)作為計(jì)數(shù)器和屎,每次調(diào)用countDown方法時(shí)N就會(huì)減1,調(diào)用await方法會(huì)阻塞當(dāng)前線程春瞬,直到N變?yōu)?柴信。
? ? ? ?線程A調(diào)用countDown方法happen-before線程B調(diào)用await方法
2.同步屏障CyclicBarrier
? ? ? ?可循環(huán)使用的屏障,讓一組線程到達(dá)一個(gè)屏障(同步點(diǎn))時(shí)被阻塞宽气,直到最后一個(gè)線程到達(dá)屏障随常,可以用于多線程計(jì)算數(shù)據(jù)。
? ? ? ?默認(rèn)構(gòu)造方法是CyclicBarrier(int parties)萄涯,參數(shù)表明屏障攔截的線程數(shù)量绪氛,每個(gè)線程調(diào)用await方法告訴CyclicBarrier我已經(jīng)達(dá)到屏障,然后當(dāng)前線程被阻塞
3.CyclicBarrier和CountDownLatch的區(qū)別
- CountDownLatch的計(jì)數(shù)器只能使用一次涝影,CyclicBarrier可以使用reset()方法重置枣察;
- CyclicBarrier提供方法來(lái)判斷阻塞線程是否被中斷(isBroken),獲取阻塞線程數(shù)量(getNumberWaiting)
4.信號(hào)量Semaphore
? ? ? ?用來(lái)控制同時(shí)訪問(wèn)特定資源的線程數(shù)量袄琳,通過(guò)協(xié)調(diào)各個(gè)線程询件,保證合理的使用公共資源∷舴可以用做流量控制宛琅,比如數(shù)據(jù)庫(kù)連接。
5.Exchanger
? ? ? ?交換者Exchanger用于線程間數(shù)據(jù)交換逗旁,提供一個(gè)同步點(diǎn)交換彼此數(shù)據(jù)嘿辟。可以用于遺傳算法片效、校對(duì)工作红伦。
? ? ? ?A線程通過(guò)執(zhí)行exchange()方法,并等待B線程也執(zhí)行exchange淀衣,兩個(gè)線程都到達(dá)同步點(diǎn)昙读,可以交換數(shù)據(jù)∨蚯牛可以設(shè)置超時(shí)等待蛮浑。
20.線程池
好處:
- a.降低資源開(kāi)銷:重復(fù)利用已創(chuàng)建的線程降低創(chuàng)建和銷毀造成的開(kāi)銷;
- b.提高響應(yīng)速度:任務(wù)到達(dá)時(shí)只嚣,不需要等待線程創(chuàng)建就能立即執(zhí)行沮稚;
- c.提高線程的可管理性:可以進(jìn)行統(tǒng)一分配、調(diào)度和監(jiān)控册舞;
線程池的處理流程:
- 線程池判斷核心線程池里的線程是否都在執(zhí)行任務(wù)蕴掏,如果是則進(jìn)入下一步;
- 線程池判斷工作隊(duì)列是否已經(jīng)滿,沒(méi)滿則將任務(wù)存入工作隊(duì)列盛杰,否則進(jìn)入一下步挽荡;
- 線程池判斷線程池的線程是否都處于工作狀態(tài),沒(méi)有則創(chuàng)建一個(gè)工作線程來(lái)執(zhí)行任務(wù)即供,否則執(zhí)行飽和策略徐伐;
ThreadPoolExecutor執(zhí)行executor方法分下面四種情況:
- 當(dāng)前運(yùn)行線程數(shù)少于corePoolSize,則創(chuàng)建新線程來(lái)執(zhí)行任務(wù)(需全局鎖)募狂;
- 當(dāng)前運(yùn)行線程數(shù)大于等于corePoolSize办素,則將任務(wù)加入BlockingQueue;
- 如果任務(wù)無(wú)法加入BlockingQueue(隊(duì)列已滿)祸穷,則創(chuàng)建新的線程來(lái)處理任務(wù)(需全局鎖)性穿;
- 如果創(chuàng)建新線程使當(dāng)前運(yùn)行線程超出maximumPoolSize,則任務(wù)被拒絕雷滚;
線程池的使用:
1)線程池的創(chuàng)建
new ThreadPoolExecutor (corePoolSize, maximumPoolSize, keepAliveTime, milliseconds, runnableTaskQueue, handler)
主要參數(shù):
CorePoolSize(線程池的基本大小):提交任務(wù)到線程池時(shí)需曾,線程池會(huì)創(chuàng)建一個(gè)線程來(lái)執(zhí)行任務(wù),即使其他空閑的基本線程能夠執(zhí)行新任務(wù)也會(huì)創(chuàng)建線程祈远,直到需要執(zhí)行的任務(wù)數(shù)大于線程池基本大小就不再創(chuàng)建呆万,可以通過(guò)調(diào)用prestartAllCoreThreads方法提前創(chuàng)建并啟動(dòng)所有基本線程;
RunnableTaskQueue(任務(wù)隊(duì)列):用于保存等待執(zhí)行任務(wù)的阻塞隊(duì)列车份,可以選擇ArrayBlockingQueueLinkedBlockedQueue,SynchronousQueue, PriorityBlockingQueue;
MaximumPoolSize(線程池最大數(shù)量):線程池運(yùn)行創(chuàng)建的最大數(shù)量谋减。若隊(duì)列滿且已創(chuàng)建線程數(shù)小于最大線程數(shù),線程池會(huì)再創(chuàng)建新的線程執(zhí)行任務(wù)扫沼;
ThreadFactory:用于設(shè)置線程工廠出爹,通過(guò)線程工廠給每個(gè)創(chuàng)建出來(lái)的線程設(shè)置有意義的名字;
RejectedExecutionHandler(飽和策略):當(dāng)線程池和任務(wù)隊(duì)列都滿了缎除,線程池處于飽和狀態(tài)严就,必須采取一種策略處理提交的新任務(wù)。
JDK1.5提供了四種策略:
- AbortPolicy:直接拋出異常器罐;
- CallerRunsPolicy:只用調(diào)用者所在線程來(lái)運(yùn)行任務(wù)梢为;
- DisCardOldestPolicy:丟棄隊(duì)列里最近的一個(gè)任務(wù),并執(zhí)行當(dāng)前任務(wù)轰坊;
- DiscardPolicy:直接丟棄铸董,不做處理;f.KeppAliveTime(線程活動(dòng)保持時(shí)間):線程池的工作線程空閑后衰倦,保持存活的時(shí)間袒炉,如果任務(wù)很多旁理,任務(wù)執(zhí)行時(shí)間短樊零,可以調(diào)大來(lái)提供線程存活;
g.TimeUnit(線程活動(dòng)保持時(shí)間的單位):DAYS, HOURS, MINUTES等