JAVA多線(xiàn)程與并發(fā)學(xué)習(xí)總結(jié)分析
1.計(jì)算機(jī)系統(tǒng)
使用高速緩存來(lái)作為內(nèi)存與處理器之間的緩沖,將運(yùn)算需要用到的數(shù)據(jù)復(fù)制到緩存中,讓計(jì)算能快速進(jìn)行;當(dāng)運(yùn)算結(jié)束后再?gòu)木彺嫱交貎?nèi)存之中,這樣處理器就無(wú)需等待緩慢的內(nèi)存讀寫(xiě)了。
緩存一致性:多處理器系統(tǒng)中,因?yàn)楣蚕硗恢鲀?nèi)存,當(dāng)多個(gè)處理器的運(yùn)算任務(wù)都設(shè)計(jì)到同一塊內(nèi)存區(qū)域時(shí),將可能導(dǎo)致各自的緩存數(shù)據(jù)不一致的情況,則同步回主內(nèi)存時(shí)需要遵循一些協(xié)議。
亂序執(zhí)行優(yōu)化:為了使得處理器內(nèi)部的運(yùn)算單位能盡量被充分利用。
2.JAVA內(nèi)存模型
目標(biāo)是定義程序中各個(gè)變量的訪(fǎng)問(wèn)規(guī)則。(包括實(shí)例字段、靜態(tài)字段和構(gòu)成數(shù)組的元素,不包括局部變量和方法參數(shù))
1.所有的變量都存儲(chǔ)在主內(nèi)存中(虛擬機(jī)內(nèi)存的一部分)。
2.每條線(xiàn)程都由自己的工作內(nèi)存,線(xiàn)程的工作內(nèi)存中保存了該線(xiàn)程使用到的變量的主內(nèi)存副本拷貝,線(xiàn)程對(duì)變量的所有操作都必須在工作內(nèi)存中進(jìn)行,而不能直接讀寫(xiě)主內(nèi)存中的變量。
3.線(xiàn)程之間無(wú)法直接訪(fǎng)問(wèn)對(duì)方的工作內(nèi)存中的變量,線(xiàn)程間變量的傳遞均需要通過(guò)主內(nèi)存來(lái)完成。
內(nèi)存間交互操作:
Lock(鎖定):作用于主內(nèi)存中的變量,把一個(gè)變量標(biāo)識(shí)為一條線(xiàn)程獨(dú)占的狀態(tài)。
Read(讀取):作用于主內(nèi)存中的變量,把一個(gè)變量的值從主內(nèi)存?zhèn)鬏數(shù)骄€(xiàn)程的工作內(nèi)存中。
Load(加載):作用于工作內(nèi)存中的變量,把read操作從主內(nèi)存中得到的變量的值放入工作內(nèi)存的變量副本中。
Use(使用):作用于工作內(nèi)存中的變量,把工作內(nèi)存中一個(gè)變量的值傳遞給執(zhí)行引擎。
Assign(賦值):作用于工作內(nèi)存中的變量,把一個(gè)從執(zhí)行引擎接收到的值賦值給工作內(nèi)存中的變量。
Store(存儲(chǔ)):作用于工作內(nèi)存中的變量,把工作內(nèi)存中的一個(gè)變量的值傳送到主內(nèi)存中。
Write(寫(xiě)入):作用于主內(nèi)存中的變量,把store操作從工作內(nèi)存中得到的變量的值放入主內(nèi)存的變量中。
Unlock(解鎖):作用于主內(nèi)存中的變量,把一個(gè)處于鎖定狀態(tài)的變量釋放出來(lái),之后可被其它線(xiàn)程鎖定。
規(guī)則:
1.不允許read和load、store和write操作之一單獨(dú)出現(xiàn)。
2.不允許一個(gè)線(xiàn)程丟棄最近的assign操作,變量在工作內(nèi)存中改變了之后必須把該變化同步回主內(nèi)存中。
3.不允許一個(gè)線(xiàn)程沒(méi)有發(fā)生過(guò)任何assign操作把數(shù)據(jù)從線(xiàn)程的工作內(nèi)存同步回主內(nèi)存中。
4.一個(gè)新的變量只能在主內(nèi)存中誕生。
5.一個(gè)變量在同一時(shí)刻只允許一條線(xiàn)程對(duì)其進(jìn)行l(wèi)ock操作,但可以被同一條線(xiàn)程重復(fù)執(zhí)行多次。
6.如果對(duì)一個(gè)變量執(zhí)行l(wèi)ock操作,將會(huì)清空工作內(nèi)存中此變量的值,在執(zhí)行引擎使用這個(gè)變量前,需要重新執(zhí)行read、load操作。
7.如果一個(gè)變量事先沒(méi)有被lock操作鎖定,則不允許對(duì)它執(zhí)行unlock操作。
8.對(duì)一個(gè)變量執(zhí)行unlock操作前,必須先把該變量同步回主內(nèi)存中。
3.volatile型變量
1.保證此變量對(duì)所有線(xiàn)程的可見(jiàn)性。每條線(xiàn)程使用此類(lèi)型變量前都需要先刷新,執(zhí)行引擎看不到不一致的情況。
運(yùn)算結(jié)果并不依賴(lài)變量的當(dāng)前值、或者確保只有單一的線(xiàn)程修改變量的值。
變量不需要與其他的狀態(tài)變量共同參與不變約束。
1.禁止指令重排序優(yōu)化。普通的變量?jī)H保證在方法執(zhí)行過(guò)程中所有依賴(lài)賦值結(jié)果的地方都能獲取到正確的結(jié)果。而不能保證賦值操作的順序與程序代碼中的順序一致。
2.load必須與use同時(shí)出現(xiàn);assign和store必須同時(shí)出現(xiàn)。
4.原子性、可見(jiàn)性與有序性
原子性:基本數(shù)據(jù)類(lèi)型的訪(fǎng)問(wèn)讀寫(xiě)是具備原子性的,synchronized塊之間的操作也具備原子性。
可見(jiàn)性:指當(dāng)一個(gè)線(xiàn)程修改了共享變量的值,其他線(xiàn)程能夠立即得知這個(gè)修改。synchronized(規(guī)則8)和final可以保證可見(jiàn)性。Final修飾的字段在構(gòu)造器中一旦被初始化完成,并且構(gòu)造器沒(méi)有把this的引用傳遞出去,那么在其他線(xiàn)程中就能看見(jiàn)final字段的值。
有序性:volatile本身包含了禁止指令重排序的語(yǔ)義,而synchronized則是由規(guī)則5獲得的,這個(gè)規(guī)則決定了持有同一個(gè)所的兩個(gè)同步塊只能串行地進(jìn)入。
5.先行發(fā)生原則
Java內(nèi)存模型中定義的兩項(xiàng)操作之間的偏序關(guān)系,如果操作A先行發(fā)生于操作B,其實(shí)就是說(shuō)在發(fā)生操作B之前,操作A產(chǎn)生的影響能被操作B觀察到。
程序次序規(guī)則:在一個(gè)線(xiàn)程內(nèi),按照代碼控制流順序,在前面的操作先行發(fā)生于后面的操作。
管程鎖定規(guī)則:一個(gè)unlock操作先行發(fā)生于后面對(duì)同一個(gè)鎖的lock操作。
Volatile變量規(guī)則:對(duì)一個(gè)volatile變量的寫(xiě)操作先行發(fā)生于后面對(duì)這個(gè)變量的讀操作。
線(xiàn)程啟動(dòng)規(guī)則:Thread對(duì)象的start()方法先行發(fā)生于此線(xiàn)程的每個(gè)操作。
線(xiàn)程終止規(guī)則:線(xiàn)程中的所有操作都先行發(fā)生于對(duì)此線(xiàn)程的終止檢測(cè)。
線(xiàn)程中斷規(guī)則:對(duì)線(xiàn)程的interrupt()方法的調(diào)用先行發(fā)生于被中斷線(xiàn)程的代碼檢測(cè)中斷事件的發(fā)生。
對(duì)象終結(jié)過(guò)則:一個(gè)對(duì)象的初始化完成先行發(fā)生于它的finalize()方法的開(kāi)始。
傳遞性:如果操作A先行發(fā)生于操作B,操作B現(xiàn)象發(fā)生于操作C,那么就可以得出操作A先行發(fā)生于操作C的結(jié)論。
時(shí)間上的先后順序與先行發(fā)生原則之間基本上沒(méi)有太大的關(guān)系。
6.線(xiàn)程實(shí)現(xiàn)
使用內(nèi)核線(xiàn)程實(shí)現(xiàn):
內(nèi)核線(xiàn)程Kernel Thread:直接由操作系統(tǒng)內(nèi)核支持的線(xiàn)程,這種線(xiàn)程由內(nèi)核類(lèi)完成線(xiàn)程切換,內(nèi)核通過(guò)操縱調(diào)度器對(duì)線(xiàn)程進(jìn)行調(diào)度,并負(fù)責(zé)將線(xiàn)程的任務(wù)映射到各個(gè)處理器上。
輕量級(jí)進(jìn)程Light Weight Process:每個(gè)輕量級(jí)進(jìn)程都由一個(gè)內(nèi)核線(xiàn)程支持。
局限性:各種進(jìn)程操作都需要進(jìn)行系統(tǒng)調(diào)用(系統(tǒng)調(diào)用代價(jià)相對(duì)較高,需要在用戶(hù)態(tài)和內(nèi)核態(tài)中來(lái)回切換);輕量級(jí)進(jìn)程要消耗一定的內(nèi)核資源,一次一個(gè)系統(tǒng)支持輕量級(jí)進(jìn)程的數(shù)量是有限的。
使用用戶(hù)線(xiàn)程實(shí)現(xiàn):
用戶(hù)線(xiàn)程:完全建立在用戶(hù)空間的線(xiàn)程庫(kù)上,系統(tǒng)內(nèi)核不能直接感知到線(xiàn)程存在的實(shí)現(xiàn)。用戶(hù)線(xiàn)程的建立、同步、銷(xiāo)毀和調(diào)度完全在用戶(hù)態(tài)中完成,不需要內(nèi)核的幫助。所有的線(xiàn)程操作都需要用戶(hù)程序自己處理。
混合實(shí)現(xiàn):
將內(nèi)核線(xiàn)程和用戶(hù)線(xiàn)程一起使用的方式。操作系統(tǒng)提供支持的輕量級(jí)進(jìn)程則作為用戶(hù)線(xiàn)程和內(nèi)核線(xiàn)程之間的橋梁。
Sun JDK,它的Windows版和Linux版都是使用一對(duì)一的線(xiàn)程模型來(lái)實(shí)現(xiàn)的,一條Java線(xiàn)程映射到一條輕量級(jí)進(jìn)程之中。
7.線(xiàn)程調(diào)度
線(xiàn)程調(diào)度是指系統(tǒng)為線(xiàn)程分配處理器使用權(quán)的過(guò)程:協(xié)同式、搶占式。
協(xié)同式:線(xiàn)程的執(zhí)行時(shí)間由線(xiàn)程本身控制,線(xiàn)程把自己的工作執(zhí)行完了之后,要主動(dòng)通知系統(tǒng)切換到另一個(gè)線(xiàn)程上。壞處:線(xiàn)程執(zhí)行時(shí)間不可控制。
搶占式:每個(gè)線(xiàn)程將由系統(tǒng)來(lái)分配執(zhí)行時(shí)間,線(xiàn)程的切換不由線(xiàn)程本身來(lái)決定。Java使用該種調(diào)用方式。
線(xiàn)程優(yōu)先級(jí):在一些平臺(tái)上(操作系統(tǒng)線(xiàn)程優(yōu)先級(jí)比Java線(xiàn)程優(yōu)先級(jí)少)不同的優(yōu)先級(jí)實(shí)際會(huì)變得相同;優(yōu)先級(jí)可能會(huì)被系統(tǒng)自行改變。
8.線(xiàn)程狀態(tài)
線(xiàn)程狀態(tài):
新建NEW:
運(yùn)行RUNNABLE:
無(wú)限期等待WAITING:等得其他線(xiàn)程顯式地喚醒。
沒(méi)有設(shè)置Timeout參數(shù)的Object.wait();沒(méi)有設(shè)置Timeout參數(shù)的Thread.wait()。
限期等待TIMED_WAITING:在一定時(shí)間之后會(huì)由系統(tǒng)自動(dòng)喚醒。
設(shè)置Timeout參數(shù)的Object.wait();設(shè)置Timeout參數(shù)的Thread.wait();Thread.sleep()方法。
阻塞BLOCKED:等待獲取一個(gè)排它鎖,等待進(jìn)入一個(gè)同步區(qū)域。
結(jié)束TERMINATED:
9.線(xiàn)程安全
線(xiàn)程安全:當(dāng)多個(gè)線(xiàn)程訪(fǎng)問(wèn)一個(gè)對(duì)象時(shí),如果不用考慮這些線(xiàn)程在運(yùn)行時(shí)環(huán)境下的調(diào)度和交換執(zhí)行,也不需要進(jìn)行額外的同步,或者調(diào)用方進(jìn)行任何其他的協(xié)調(diào)操作,調(diào)用這個(gè)對(duì)象的行為都可以獲得正確的結(jié)果,那這個(gè)對(duì)象就是線(xiàn)程安全的。
不可變:只要一個(gè)不可變的對(duì)象被正確地構(gòu)建出來(lái)。使用final關(guān)鍵字修飾的基本數(shù)據(jù)類(lèi)型;如果共享數(shù)據(jù)是一個(gè)對(duì)象,那就需要保證對(duì)象的行為不會(huì)對(duì)其狀態(tài)產(chǎn)生任何影響(String類(lèi)的對(duì)象)。方法:把對(duì)象中帶有狀態(tài)的變量都申明為final,如Integer類(lèi)。有:枚舉類(lèi)型、Number的部分子類(lèi)(AtomicInteger和AtomicLong除外)。
絕對(duì)線(xiàn)程安全:
相對(duì)線(xiàn)程安全:對(duì)這個(gè)對(duì)象單獨(dú)的操作是線(xiàn)程安全的。一般意義上的線(xiàn)程安全。
線(xiàn)程兼容:需要通過(guò)調(diào)用端正確地使用同步手段來(lái)保證對(duì)象在并發(fā)環(huán)境中安全地使用。
線(xiàn)程對(duì)立:不管調(diào)用端是否采取了同步措施,都無(wú)法在多線(xiàn)程環(huán)境中并發(fā)使用的代碼。有:System.setIn()、System.setOut()、System.runFinalizersOnExit()
10. 線(xiàn)程安全的實(shí)現(xiàn)方法
1.1.互斥同步:同步是指在多個(gè)線(xiàn)程并發(fā)訪(fǎng)問(wèn)共享數(shù)據(jù)時(shí),保證共享數(shù)據(jù)在同一個(gè)時(shí)刻只被一條線(xiàn)程使用?;コ夥绞剑号R界區(qū)、互斥量和信號(hào)量。
Synchronized關(guān)鍵字:編譯后會(huì)在同步塊前后分別形成monitorenter和monitorexit這兩個(gè)字節(jié)碼指令。這兩個(gè)指令都需要一個(gè)引用類(lèi)型的參數(shù)來(lái)指明要鎖定和解鎖的對(duì)象。如果沒(méi)有明確指定對(duì)象參數(shù),那就根據(jù)synchronized修飾的是實(shí)例方法還是類(lèi)方法,去取對(duì)應(yīng)的對(duì)象實(shí)例或Class對(duì)象來(lái)作為鎖對(duì)象。
在執(zhí)行monitorenter指令時(shí),首先嘗試獲取對(duì)象的鎖,如果沒(méi)有被鎖定或者當(dāng)前線(xiàn)程已經(jīng)擁有了該對(duì)象的鎖,則將鎖計(jì)數(shù)器加1,相應(yīng)的執(zhí)行moniterexit時(shí),將鎖計(jì)數(shù)器減1,當(dāng)計(jì)數(shù)器為0時(shí),鎖就被釋放了。如果獲取對(duì)象鎖失敗,則當(dāng)前線(xiàn)程就要阻塞等待。
ReentrantLock相對(duì)synchronized的高級(jí)功能:
等待可中斷:當(dāng)持有鎖的線(xiàn)程長(zhǎng)期不釋放鎖時(shí),正在等待的線(xiàn)程可以選擇放棄等待,改為處理其他事情。
公平鎖:多個(gè)線(xiàn)程在等待同一個(gè)鎖時(shí),必須按照申請(qǐng)鎖的事件順序來(lái)一次獲取鎖;而非公平鎖在被釋放時(shí),任何一個(gè)等待鎖的線(xiàn)程都有機(jī)會(huì)獲得鎖。Synchronized中的鎖是非公平鎖,ReentrantLock默認(rèn)也是非公平鎖。
鎖綁定多個(gè)條件:一個(gè)ReentrantLock對(duì)象可以同時(shí)綁定多個(gè)Condition對(duì)象。
1.2. 非阻塞同步:
基于沖突檢測(cè)的樂(lè)觀并發(fā)策略:先進(jìn)行操作,如果沒(méi)有其他線(xiàn)程爭(zhēng)用共享數(shù)據(jù),那操作就成功了;如果共享數(shù)據(jù)有爭(zhēng)用,產(chǎn)生了沖突,那就再進(jìn)行其他的補(bǔ)償措施(一般是不斷的嘗試,直到成功為止)。
AtomicInteger等原子類(lèi)中提供了方法實(shí)現(xiàn)了CAS指令。
1.3.無(wú)同步方案:
可重入代碼:可以在代碼執(zhí)行的任何時(shí)刻中斷它,轉(zhuǎn)而去執(zhí)行另一段代碼,而在控制權(quán)返回后,原來(lái)的程序不會(huì)出現(xiàn)任何錯(cuò)誤。特征:不依賴(lài)存儲(chǔ)在堆上的數(shù)據(jù)和公用的系統(tǒng)資源、用到的狀態(tài)量都由參數(shù)傳入,不調(diào)用非可重入的方法等。如果一個(gè)方法,它的返回結(jié)果是可以預(yù)測(cè)的,只要出入了相同的數(shù)據(jù),就能返回相同的結(jié)果,那它就滿(mǎn)足可重入性的要求。
線(xiàn)程本地存儲(chǔ):如果一段代碼中所需要的數(shù)據(jù)必須與其它代碼共享,那就看看這些共享數(shù)據(jù)的代碼是否能保證在同一個(gè)線(xiàn)程中執(zhí)行。
A.ThreadLocal類(lèi)
ThreadLocal:線(xiàn)程級(jí)別的局部變量,為每個(gè)使用該變量的線(xiàn)程提供一個(gè)獨(dú)立的變量副本,每個(gè)線(xiàn)程修改副本時(shí)不影響其他線(xiàn)程對(duì)象的副本。ThreadLocal實(shí)例通常作為靜態(tài)私有字段出現(xiàn)在一個(gè)類(lèi)中。
11.鎖優(yōu)化
1.1.自旋鎖
為了讓線(xiàn)程等待,讓線(xiàn)程執(zhí)行一個(gè)忙循環(huán)(自旋)。需要物理機(jī)器有一個(gè)以上的處理器。自旋等待雖然避免了線(xiàn)程切換的開(kāi)銷(xiāo),帶它是要占用處理器時(shí)間的,所以如果鎖被占用的時(shí)間很短,自旋等待的效果就會(huì)非常好,反之自旋的線(xiàn)程只會(huì)白白消耗處理器資源。自旋次數(shù)的默認(rèn)值是10次,可以使用參數(shù)-XX:PreBlockSpin來(lái)更改。
自適應(yīng)自旋鎖:自旋的時(shí)間不再固定,而是由前一次在同一個(gè)鎖上的自旋時(shí)間及鎖的擁有者的狀態(tài)來(lái)決定。
1.2.鎖清除
指虛擬機(jī)即時(shí)編譯器在運(yùn)行時(shí),對(duì)一些代碼上要求同步,但是被檢測(cè)到不可能存在共享數(shù)據(jù)競(jìng)爭(zhēng)的鎖進(jìn)行清除(逃逸分析技術(shù):在堆上的所有數(shù)據(jù)都不會(huì)逃逸出去被其它線(xiàn)程訪(fǎng)問(wèn)到,可以把它們當(dāng)成棧上數(shù)據(jù)對(duì)待)。
1.3.鎖粗化
如果虛擬機(jī)探測(cè)到有一串零碎的操作都對(duì)同一個(gè)對(duì)象加鎖,將會(huì)把加鎖同步的范圍擴(kuò)展到整個(gè)操作序列的外部。
HotSpot虛擬機(jī)的對(duì)象的內(nèi)存布局:對(duì)象頭(Object Header)分為兩部分信息嗎,第一部分(Mark Word)用于存儲(chǔ)對(duì)象自身的運(yùn)行時(shí)數(shù)據(jù),另一個(gè)部分用于存儲(chǔ)指向方法區(qū)對(duì)象數(shù)據(jù)類(lèi)型的指針,如果是數(shù)組的話(huà),還會(huì)由一個(gè)額外的部分用于存儲(chǔ)數(shù)組的長(zhǎng)度。
32位HotSpot虛擬機(jī)中對(duì)象未被鎖定的狀態(tài)下,Mark Word的32個(gè)Bits空間中25位用于存儲(chǔ)對(duì)象哈希碼,4位存儲(chǔ)對(duì)象分代年齡,2位存儲(chǔ)鎖標(biāo)志位,1位固定為0。
HotSpot虛擬機(jī)對(duì)象頭Mark Word
存儲(chǔ)內(nèi)容 |
標(biāo)志位 |
狀態(tài) |
對(duì)象哈希碼、對(duì)象分代年齡 |
01 |
未鎖定 |
指向鎖記錄的指針 |
00 |
輕量級(jí)鎖定 |
指向重量級(jí)鎖的指針 |
10 |
膨脹(重量級(jí)鎖) |
空,不記錄信息 |
11 |
GC標(biāo)記 |
偏向線(xiàn)程ID,偏向時(shí)間戳、對(duì)象分代年齡 |
01 |
可偏向 |
1.4. 輕量級(jí)鎖
在代碼進(jìn)入同步塊時(shí),如果此同步對(duì)象沒(méi)有被鎖定,虛擬機(jī)首先將在當(dāng)前線(xiàn)程的棧幀中建立一個(gè)名為鎖記錄(Lock Record)的空間,用于存儲(chǔ)所對(duì)象目前的Mark Word的拷貝。然后虛擬機(jī)將使用CAS操作嘗試將對(duì)象的Mark Word更新為執(zhí)行Lock Record的指針。如果成功,那么這個(gè)線(xiàn)程就擁有了該對(duì)象的鎖。如果更新操作失敗,虛擬機(jī)首先會(huì)檢查對(duì)象的Mark Word是否指向當(dāng)前線(xiàn)程的棧幀,如果是就說(shuō)明當(dāng)前線(xiàn)程已經(jīng)擁有了這個(gè)對(duì)象的鎖,否則說(shuō)明這個(gè)對(duì)象已經(jīng)被其它線(xiàn)程搶占。如果有兩條以上的線(xiàn)程爭(zhēng)用同一個(gè)鎖,那輕量級(jí)鎖就不再有效,要膨脹為重量級(jí)鎖。
解鎖過(guò)程:如果對(duì)象的Mark Word仍然指向著線(xiàn)程的鎖記錄,那就用CAS操作把對(duì)象當(dāng)前的Mark Word和和線(xiàn)程中復(fù)制的Displaced Mark Word替換回來(lái),如果替換成功,整個(gè)過(guò)程就完成。如果失敗,說(shuō)明有其他線(xiàn)程嘗試過(guò)獲取該鎖,那就要在釋放鎖的同時(shí),喚醒被掛起的線(xiàn)程。
輕量級(jí)鎖的依據(jù):對(duì)于絕大部分的鎖,在整個(gè)同步周期內(nèi)都是不存在競(jìng)爭(zhēng)的。
傳統(tǒng)鎖(重量級(jí)鎖)使用操作系統(tǒng)互斥量來(lái)實(shí)現(xiàn)的。
1.5. 偏向鎖
目的是消除在無(wú)競(jìng)爭(zhēng)情況下的同步原語(yǔ),進(jìn)一步提高程序的運(yùn)行性能。鎖會(huì)偏向第一個(gè)獲得它的線(xiàn)程,如果在接下來(lái)的執(zhí)行過(guò)程中,該鎖沒(méi)有被其它線(xiàn)程獲取,則持有鎖的線(xiàn)程將永遠(yuǎn)不需要再進(jìn)行同步。
當(dāng)鎖第一次被線(xiàn)程獲取的時(shí)候,虛擬機(jī)將會(huì)把對(duì)象頭中的標(biāo)志位設(shè)為01,同時(shí)使用CAS操作把獲取到這個(gè)鎖的線(xiàn)程的ID記錄在對(duì)象的Mark Word之中,如果成功,持有偏向鎖的線(xiàn)程以后每次進(jìn)入這個(gè)鎖相關(guān)的同步塊時(shí),都可以不進(jìn)行任何同步操作。
當(dāng)有另一個(gè)線(xiàn)程去嘗試獲取這個(gè)鎖時(shí),偏向模式就宣告結(jié)束。根據(jù)所對(duì)象目前是否處于被鎖定的狀態(tài),撤銷(xiāo)偏向后恢復(fù)到未鎖定或輕量級(jí)鎖定狀態(tài)。
12.內(nèi)核態(tài)和用戶(hù)態(tài)
操作系統(tǒng)的兩種運(yùn)行級(jí)別,intel cpu提供-Ring3三種運(yùn)行模式。
Ring0是留給操作系統(tǒng)代碼,設(shè)備驅(qū)動(dòng)程序代碼使用的,它們工作于系統(tǒng)核心態(tài);而Ring3則給普通的用戶(hù)程序使用,它們工作在用戶(hù)態(tài)。運(yùn)行于處理器核心態(tài)的代碼不受任何的限制,可以自由地訪(fǎng)問(wèn)任何有效地址,進(jìn)行直接端口訪(fǎng)問(wèn)。而運(yùn)行于用戶(hù)態(tài)的代碼則要受到處理器的諸多檢查,它們只能訪(fǎng)問(wèn)映射其地址空間的頁(yè)表項(xiàng)中規(guī)定的在用戶(hù)態(tài)下可訪(fǎng)問(wèn)頁(yè)面的虛擬地址,且只能對(duì)任務(wù)狀態(tài)段(TSS)中I/O許可位圖(I/O Permission Bitmap)中規(guī)定的可訪(fǎng)問(wèn)端口進(jìn)行直接訪(fǎng)問(wèn)。
13. 常用方法
1.1.object.wait():
在其他線(xiàn)程調(diào)用此對(duì)象的notify()或者notifyAll()方法,或超過(guò)指定時(shí)間量前,當(dāng)前線(xiàn)程T等待(線(xiàn)程T必須擁有該對(duì)象的鎖)。線(xiàn)程T被放置在該對(duì)象的休息區(qū)中,并釋放鎖。在被喚醒、中斷、超時(shí)的情況下,從對(duì)象的休息區(qū)中刪除線(xiàn)程T,并重新進(jìn)行線(xiàn)程調(diào)度。一旦線(xiàn)程T獲得該對(duì)象的鎖,該對(duì)象上的所有同步申明都被恢復(fù)到調(diào)用wait()方法時(shí)的狀態(tài),然后線(xiàn)程T從wait()方法返回。如果當(dāng)前線(xiàn)程在等待之前或在等待時(shí)被任何線(xiàn)程中斷,則會(huì)拋出 InterruptedException。在按上述形式恢復(fù)此對(duì)象的鎖定狀態(tài)時(shí)才會(huì)拋出此異常。在拋出此異常時(shí),當(dāng)前線(xiàn)程的中斷狀態(tài)被清除。
只有該對(duì)象的鎖被釋放,并不會(huì)釋放當(dāng)前線(xiàn)程持有的其他同步資源。
1.2. object.notify()
喚醒在此對(duì)象鎖上等待的單個(gè)線(xiàn)程。此方法只能由擁有該對(duì)象鎖的線(xiàn)程來(lái)調(diào)用。
1.3. Thread.sleep()
在指定的毫秒數(shù)內(nèi)讓當(dāng)前正在執(zhí)行的線(xiàn)程休眠(暫停執(zhí)行),此操作受到系統(tǒng)計(jì)時(shí)器和調(diào)度程序精度和準(zhǔn)確性的影響。監(jiān)控狀態(tài)依然保持、會(huì)自動(dòng)恢復(fù)到可運(yùn)行狀態(tài),不會(huì)釋放對(duì)象鎖。如果任何線(xiàn)程中斷了當(dāng)前線(xiàn)程。當(dāng)拋出InterruptedException異常時(shí),當(dāng)前線(xiàn)程的中斷狀態(tài)被清除。讓出CPU分配的執(zhí)行時(shí)間。
thread.join():在一個(gè)線(xiàn)程對(duì)象上調(diào)用,使當(dāng)前線(xiàn)程等待這個(gè)線(xiàn)程對(duì)象對(duì)應(yīng)的線(xiàn)程結(jié)束。
Thread.yield():暫停當(dāng)前正在執(zhí)行的線(xiàn)程對(duì)象,并執(zhí)行其他線(xiàn)程。
thread.interrupt()
中斷線(xiàn)程,停止其正在進(jìn)行的一切。中斷一個(gè)不處于活動(dòng)狀態(tài)的線(xiàn)程不會(huì)有任何作用。
如果線(xiàn)程在調(diào)用Object類(lèi)的wait()方法、或者join()、sleep()方法過(guò)程中受阻,則其中斷狀態(tài)將被清除,并收到一個(gè)InterruptedException。
Thread.interrupted():檢測(cè)當(dāng)前線(xiàn)程是否已經(jīng)中斷,并且清除線(xiàn)程的中斷狀態(tài)(回到非中斷狀態(tài))。
thread.isAlive():如果線(xiàn)程已經(jīng)啟動(dòng)且尚未終止,則為活動(dòng)狀態(tài)。
thread.setDaemon():需要在start()方法調(diào)用之前調(diào)用。當(dāng)正在運(yùn)行的線(xiàn)程都是后臺(tái)線(xiàn)程時(shí),Java虛擬機(jī)將退出。否則當(dāng)主線(xiàn)程退出時(shí),其他線(xiàn)程仍然會(huì)繼續(xù)執(zhí)行。
14.其他
1.當(dāng)調(diào)用Object的wait()、notify()、notifyAll()時(shí),如果當(dāng)前線(xiàn)程沒(méi)有獲得該對(duì)象鎖,則會(huì)拋出IllegalMonitorStateException異常。
1.如果一個(gè)方法申明為synchronized,則等同于在這個(gè)方法上調(diào)用synchronized(this)。
如果一個(gè)靜態(tài)方法被申明為synchronized,則等同于在這個(gè)方法上調(diào)用synchronized(類(lèi).class)。當(dāng)一個(gè)線(xiàn)程進(jìn)入同步靜態(tài)方法中時(shí),其他線(xiàn)程不能進(jìn)入這個(gè)類(lèi)的任何靜態(tài)同步方法。
1.線(xiàn)程成為對(duì)象鎖的擁有者:
1.通過(guò)執(zhí)行此對(duì)象的同步實(shí)例方法
2.通過(guò)執(zhí)行在此對(duì)象上進(jìn)行同步的synchronized語(yǔ)句的正文
3.對(duì)于Class類(lèi)型的對(duì)象,可以通過(guò)執(zhí)行該類(lèi)的同步靜態(tài)方法。
1.死鎖:
死鎖就是兩個(gè)或兩個(gè)以上的線(xiàn)程被無(wú)限的阻塞,線(xiàn)程之間相互等待所需資源。
可能發(fā)生在以下情況:
當(dāng)兩個(gè)線(xiàn)程相互調(diào)用Thread.join();
當(dāng)兩個(gè)線(xiàn)程使用嵌套的同步塊,一個(gè)線(xiàn)程占用了另外一個(gè)線(xiàn)程必須的鎖,互相等待時(shí)被阻塞就有可能出現(xiàn)死鎖。
1.調(diào)用了Thread類(lèi)的start()方法(向CPU申請(qǐng)另一個(gè)線(xiàn)程空間來(lái)執(zhí)行run()方法里的代碼),線(xiàn)程的run()方法不一定立即執(zhí)行,而是要等待JVM進(jìn)行調(diào)度。
run()方法中包含的是線(xiàn)程的主體,也就是這個(gè)線(xiàn)程被啟動(dòng)后將要運(yùn)行的代碼。
相關(guān)文章
Javafx實(shí)現(xiàn)國(guó)際象棋游戲
這篇文章主要為大家詳細(xì)介紹了Javafx實(shí)現(xiàn)國(guó)際象棋游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-05-05Java數(shù)據(jù)結(jié)構(gòu)之集合框架與常用算法詳解
Java集合框架是Java中常用的數(shù)據(jù)結(jié)構(gòu)庫(kù),包括List、Set、Map等多種數(shù)據(jù)結(jié)構(gòu),支持快速的元素添加、刪除、查找等操作,可以用于解決各種實(shí)際問(wèn)題。Java中也有多種常用算法,如排序、查找、遞歸等,在數(shù)據(jù)處理和分析中有廣泛應(yīng)用2023-04-04SpringBoot+MyBatisPlus+Vue 前后端分離項(xiàng)目快速搭建過(guò)程(后端)
這篇文章主要介紹了SpringBoot+MyBatisPlus+Vue 前后端分離項(xiàng)目快速搭建過(guò)程(后端),快速生成后端代碼、封裝結(jié)果集、增刪改查、模糊查找,畢設(shè)基礎(chǔ)框架,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-05-05Java實(shí)現(xiàn)從字符串中找出數(shù)字字符串的方法小結(jié)
這篇文章主要介紹了Java實(shí)現(xiàn)從字符串中找出數(shù)字字符串的方法,結(jié)合實(shí)例形式總結(jié)分析了Java查找數(shù)字字符串的常用技巧,需要的朋友可以參考下2016-03-03Java使用volatile關(guān)鍵字的注意事項(xiàng)
volatile關(guān)鍵字是Java中的一種稍弱的同步機(jī)制,為什么稱(chēng)之為弱機(jī)制。這篇文章主要介紹了Java使用volatile關(guān)鍵字的注意事項(xiàng),需要的朋友可以參考下2017-02-02Java實(shí)現(xiàn)每日給女友微信發(fā)送早安信息
這篇文章主要為大家詳細(xì)介紹了Java如何實(shí)現(xiàn)每日給女友微信發(fā)送早安等微信信息,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的可以了解一下2022-12-12SpringBoot實(shí)現(xiàn)優(yōu)雅停機(jī)的正確方法
什么叫優(yōu)雅停機(jī)?就是向應(yīng)用進(jìn)程發(fā)出停止指令之后,能保證正在執(zhí)行的業(yè)務(wù)操作不受影響,直到操作運(yùn)行完畢之后再停止服務(wù)。本文就來(lái)和大家聊聊SpringBoot實(shí)現(xiàn)優(yōu)雅停機(jī)的正確姿勢(shì),希望對(duì)大家有所幫助2023-01-01mybatis plus開(kāi)發(fā)過(guò)程中遇到的問(wèn)題記錄及解決
這篇文章主要介紹了mybatis plus開(kāi)發(fā)過(guò)程中遇到的問(wèn)題記錄及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07