java同步鎖講解_第1頁
java同步鎖講解_第2頁
java同步鎖講解_第3頁
java同步鎖講解_第4頁
java同步鎖講解_第5頁
已閱讀5頁,還剩7頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)

文檔簡介

1、咱們能夠在運算機上運行各類運算機軟件程序。每一個運行的程序可能包括多個獨立運行的線程(Thread)。線程(Thread)是一份獨立運行的程序,有自己專用的運行棧。線程有可能和其他線程共享一些資源,比如,內(nèi)存,文件,數(shù)據(jù)庫等。當(dāng)多個線程同時讀寫同一份共享資源的時候,可能會引發(fā)沖突。這時,咱們需要引入線程“同步”機制,即列位線程之間要有個先來后到,不能一窩蜂擠上去搶作一團。同步那個詞是從英文synchronize(使同時發(fā)生)翻譯過來的。我也不明白什么緣故要用那個很容易引發(fā)誤解的詞。既然大伙兒都這么用,咱們也就只好這么將就。線程同步的真實意思和字面意思恰好相反。線程同步的真實意思,實際上是“排隊

2、”:幾個線程之間要排隊,一個一個對共享資源進行操作,而不是同時進行操作。因此,關(guān)于線程同步,需要牢牢記住的第一點是:線程同步確實是線程排隊。同步確實是排隊。線程同步的目的確實是幸免線程“同步”執(zhí)行。這可真是個無聊的繞口令。關(guān)于線程同步,需要牢牢記住的第二點是“共享”這兩個字。只有共享資源的讀寫訪問才需要同步。若是不是共享資源,那么就全然沒有同步的必要。關(guān)于線程同步,需要牢牢記住的第三點是,只有“變量”才需要同步訪問。若是共享的資源是固定不變的,那么就相當(dāng)于“常量”,線程同時讀取常量也不需要同步。至少一個線程修改共享資源,如此的情形下,線程之間就需要同步。關(guān)于線程同步,需要牢牢記住的第四點是:多

3、個線程訪問共享資源的代碼有可能是同一份代碼,也有可能是不同的代碼;不管是不是執(zhí)行同一份代碼,只要這些線程的代碼訪問同一份可變的共享資源,這些線程之間就需要同步。為了加深明白得,下面舉幾個例子。有兩個采購員,他們的工作內(nèi)容是相同的,都是遵循如下的步驟:(1)到市場上去,尋覓并購買有潛力的樣品。(2)回到公司,寫報告。這兩個人的工作內(nèi)容盡管一樣,他們都需要購買樣品,他們可能買到一樣種類的樣品,可是他們絕對可不能購買到同一件樣品,他們之間沒有任何共享資源。因此,他們能夠各行其是自己的工作,互不干擾。這兩個采購員就相當(dāng)于兩個線程;兩個采購員遵循相同的工作步驟,相當(dāng)于這兩個線程執(zhí)行同一段代碼。下面給這兩

4、個采購員增加一個工作步驟。采購員需要依照公司的“布告欄”上面發(fā)布的信息,安排自己的工作打算。這兩個采購員有可能同時走到布告欄的前面,同時觀看布告欄上的信息。這一點問題都沒有。因為布告欄是只讀的,這兩個采購員誰都可不能去修改布告欄上寫的信息。下面增加一個角色。一個辦公室行政人員那個時候,也走到了布告欄前面,預(yù)備修改布告欄上的信息。若是行政人員先抵達(dá)布告欄,而且正在修改布告欄的內(nèi)容。兩個采購員那個時候,恰好也到了。這兩個采購員就必需等待行政人員完成修改以后,才能觀看修改后的信息。若是行政人員抵達(dá)的時候,兩個采購員已經(jīng)在觀看布告欄了。那么行政人員需要等待兩個采購員把當(dāng)前信息記錄下來以后,才能夠?qū)懮闲?/p>

5、的信息。上述這兩種情形,行政人員和采購員對布告欄的訪問就需要進行同步。因為其中一個線程(行政人員)修改了共享資源(布告欄)。而且咱們能夠看到,行政人員的工作流程和采購員的工作流程(執(zhí)行代碼)完全不同,可是由于他們訪問了同一份可變共享資源(布告欄),因此他們之間需要同步。同步鎖前面講了什么緣故要線程同步,下面咱們就來看如何才能線程同步。線程同步的大體實現(xiàn)思路仍是比較容易明白得的。咱們能夠給共享資源加一把鎖,這把鎖只有一把鑰匙。哪個線程獲取了這把鑰匙,才有權(quán)利訪問該共享資源。生活中,咱們也可能會碰到如此的例子。一些超市的外面提供了一些自動儲物箱。每一個儲物箱都有一把鎖,一把鑰匙。人們能夠利用那些帶

6、有鑰匙的儲物箱,把東西放到儲物箱里面,把儲物箱鎖上,然后把鑰匙拿走。如此,該儲物箱就被鎖住了,其他人不能再訪問那個儲物箱。(固然,真實的儲物箱鑰匙是能夠被人拿走復(fù)制的,因此不要把珍貴物品放在超市的儲物箱里面。于是很多超市都采納了電子密碼鎖。)線程同步鎖那個模型看起來很直觀??墒牵€有一個嚴(yán)峻的問題沒有解決,那個同步鎖應(yīng)該加在哪里?固然是加在共享資源上了。反映快的讀者必然會搶先回答。沒錯,若是可能,咱們固然盡可能把同步鎖加在共享資源上。一些比較完善的共享資源,比如,文件系統(tǒng),數(shù)據(jù)庫系統(tǒng)等,自身都提供了比較完善的同步鎖機制。咱們不用另外給這些資源加鎖,這些資源自己就有鎖??墒?,大部份情形下,咱們在

7、代碼中訪問的共享資源都是比較簡單的共享對象。這些對象里面沒有地址讓咱們加鎖。讀者可能會提出建議:什么緣故不在每一個對象內(nèi)部都增加一個新的區(qū)域,專門用來加鎖呢?這種設(shè)計理論被騙然也是可行的。問題在于,線程同步的情形并非是很普遍。若是因為這小概率事件,在所有對象內(nèi)部都開辟一塊鎖空間,將會帶來極大的空間浪費。得不償失。于是,現(xiàn)代的編程語言的設(shè)計思路都是把同步鎖加在代碼段上。確切的說,是把同步鎖加在“訪問共享資源的代碼段”上。這一點必然要記住,同步鎖是加在代碼段上的。同步鎖加在代碼段上,就專門好地解決了上述的空間浪費問題??墒菂s增加了模型的復(fù)雜度,也增加了咱們的明白得難度。此刻咱們就來認(rèn)真分析“同步鎖

8、加在代碼段上”的線程同步模型。第一,咱們已經(jīng)解決了同步鎖加在哪里的問題。咱們已經(jīng)確信,同步鎖不是加在共享資源上,而是加在訪問共享資源的代碼段上。第二,咱們要解決的問題是,咱們應(yīng)該在代碼段上加什么樣的鎖。那個問題是重點中的重點。這是咱們尤其要注意的問題:訪問同一份共享資源的不同代碼段,應(yīng)該加上同一個同步鎖;若是加的是不同的同步鎖,那么全然就起不到同步的作用,沒有任何意義。這確實是說,同步鎖本身也必然是多個線程之間的共享對象。Java語言的synchronized關(guān)鍵字為了加深明白得,舉幾個代碼段同步的例子。不同語言的同步鎖模型都是一樣的。只是表達(dá)方式有些不同。那個地址咱們以當(dāng)前最流行的Java語

9、言為例。Java語言里面用synchronized關(guān)鍵字給代碼段加鎖。整個語法形式表現(xiàn)為synchronized(同步鎖)/訪問共享資源,需要同步的代碼段那個地址尤其要注意的確實是,同步鎖本身必然若是共享的對象。f1()產(chǎn)生一個同步鎖Objectlock1=newObject();/synchronized(lock1)/代碼段A/訪問共享資源resource1/需要同步上面這段代碼沒有任何意義。因為那個同步鎖是在函數(shù)體內(nèi)部產(chǎn)生的。每一個線程挪用這段代碼的時候,都會產(chǎn)生一個新的同步鎖。那么多個線程之間,利用的是不同的同步鎖。全然達(dá)不到同步的目的。同步代碼必然要寫成如下的形式,才成心義。publ

10、icstaticfinalObjectlock1=newObject();f1()synchronized(lock1)/lock1是公用同步鎖/代碼段A/訪問共享資源resource1/需要同步你不必然要把同步鎖聲明為static或public,可是你必然要保證相關(guān)的同步代碼之間,必然要利用同一個同步鎖。講到那個地址,你必然會好奇,那個同步鎖究竟是個什么東西。什么緣故隨意聲明一個Object對象,就能夠夠作為同步鎖?在Java里面,同步鎖的概念確實是如此的。任何一個ObjectReference都能夠作為同步鎖。咱們能夠把ObjectReference明白得為對象在內(nèi)存分派系統(tǒng)中的內(nèi)存地址。

11、因此,要保證同步代碼段之間利用的是同一個同步鎖,咱們就要保證這些同步代碼段的synchronized關(guān)鍵字利用的是同一個ObjectReference,同一個內(nèi)存地址。這也是什么緣故我在前面的代碼中聲明lock1的時候,利用了final關(guān)鍵字,這確實是為了保證lock1的ObjectReference在整個系統(tǒng)運行進程中都維持不變。一些求知欲強的讀者可能想要繼續(xù)深切了解synchronzied(同步鎖)的實際運行機制。Java虛擬機標(biāo)準(zhǔn)中(你能夠在google用“JVMSpec”等關(guān)鍵字進行搜索),有對synchronized關(guān)鍵字的詳細(xì)說明。synchronized會編譯成monitoren

12、ter,monitorexit之類的指令對。Monitor確實是事實上的同步鎖。每一個ObjectReference在概念上都對應(yīng)一個monitor。這些實現(xiàn)細(xì)節(jié)問題,并非是明白得同步鎖模型的關(guān)鍵。咱們繼續(xù)看幾個例子,加深對同步鎖模型的明白得。publicstaticfinalObjectlock1=newObject();f1()synchronized(lock1)/lock1是公用同步鎖/代碼段A/訪問共享資源resource1/需要同步f2()synchronized(lock1)/lock1是公用同步鎖/代碼段B/訪問共享資源resource1/需要同步上述的代碼中,代碼段A和代碼段

13、B確實是同步的。因為它們利用的是同一個同步鎖lock1。若是有10個線程同時執(zhí)行代碼段A,同時還有20個線程同時執(zhí)行代碼段B,那么這30個線程之間都是要進行同步的。這30個線程都要競爭一個同步鎖lock1。同一時刻,只有一個線程能夠取得lock1的所有權(quán),只有一個線程能夠執(zhí)行代碼段A或代碼段Bo其他競爭失敗的線程只能暫停運行,進入到該同步鎖的就緒(Ready)隊列。每一個同步鎖下面都掛了幾個線程隊列,包括就緒(Ready)隊列,待召(Waiting)隊列等。比如,lock1對應(yīng)的就緒隊列就能夠夠叫做lock1-readyqueue。每一個隊列里面都可能有多個暫停運行的線程。注意,競爭同步鎖失敗

14、的線程進入的是該同步鎖的就緒(Ready)隊列,而不是后面要講述的待召隊列(WaitingQueue,也能夠翻譯為等待隊列)。就緒隊列里面的線程老是時刻預(yù)備著競爭同步鎖,時刻預(yù)備著運行。而待召隊列里面的線程那么只能一直等待,直到等到某個信號的通知以后,才能夠轉(zhuǎn)移到就緒隊列中,預(yù)備運行。成功獲取同步鎖的線程,執(zhí)行完同步代碼段以后,會釋放同步鎖。該同步鎖的就緒隊列中的其他線程就繼續(xù)下一輪同步鎖的競爭。成功者就能夠夠繼續(xù)運行,失敗者仍是要乖乖地待在就緒隊列中。因此,線程同步是超級花費資源的一種操作。咱們要盡可能操縱線程同步的代碼段范圍。同步的代碼段范圍越小越好。咱們用一個名詞“同步粒度”來表示同步代

15、碼段的范圍。同步粒度在Java語言里面,咱們能夠直接把synchronized關(guān)鍵字直接加在函數(shù)的概念上。比如。synchronizedf1()/f1代碼段這段代碼就等價于f1()synchronized(this)/同步鎖確實是對象本身/f1代碼段一樣的原那么適用于靜態(tài)(static)函數(shù)比如。staticsynchronizedf1()/f1代碼段這段代碼就等價于staticf1()synchronized()/同步鎖是類概念本身/f1代碼段可是,咱們要盡可能幸免這種直接把synchronized加在函數(shù)概念上的偷懶做法。因為咱們要操縱同步粒度。同步的代碼段越小越好。synchronize

16、d操縱的范圍越小越好。咱們不僅要在縮小同步代碼段的長度上下功夫,咱們同時還要注意細(xì)分同步鎖。比如,下面的代碼publicstaticfinalObjectlock1=newObject();f1()synchronized(lock1)/lock1是公用同步鎖/代碼段A/訪問共享資源resource1/需要同步f2()是公用同步鎖synchronized(lock1)/lock1/代碼段B/訪問共享資源resource1/需要同步f3()synchronized(lock1)/lock1是公用同步鎖/代碼段C/訪問共享資源resource2/需要同步f4()synchronized(lock1

17、)/lock1是公用同步鎖/代碼段D/訪問共享資源resource2/需要同步上述的4段同步代碼,利用同一個同步鎖lock1。所有挪用4段代碼中任何一段代碼的線程,都需要競爭同一個同步鎖lock1。咱們認(rèn)真分析一下,發(fā)覺這是沒有必要的。因為f1()的代碼段A和f2()的代碼段B訪問的共享資源是resourcel,f3()的代碼段C和f4()的代碼段D訪問的共享資源是resource2,它們沒有必要都競爭同一個同步鎖lock1。咱們能夠增加一個同步鎖lock2。f3()和f4()的代碼能夠修改成:publicstaticfinalObjectlock2=newObject();f3()是公用同步

18、鎖synchronized(lock2)/lock2/代碼段C/訪問共享資源resource2/需要同步f4()synchronized(lock2)/lock2是公用同步鎖/代碼段D/訪問共享資源resource2/需要同步如此,f1()和f2()就會競爭lock1,而f3()和f4()就會競爭lock2。如此,分開來別離競爭兩個鎖,就能夠夠大大較少同步鎖競爭的概率,從而減少系統(tǒng)的開銷。/士旦旦信號量同步鎖模型只是最簡單的同步模型。同一時刻,只有一個線程能夠運行同步代碼。有的時候,咱們希望處置加倍復(fù)雜的同步模型,比如生產(chǎn)者/消費者模型、讀寫同步模型等。這種情形下,同步鎖模型就不夠用了。咱們需

19、要一個新的模型。這確實是咱們要講述的信號量模型。信號量模型的工作方式如下:線程在運行的進程中,能夠主動停下來,等待某個信號量的通知;這時,該線程就進入到該信號量的待召(Waiting)隊列當(dāng)中;等到通知以后,再繼續(xù)運行。很多語言里面,同步鎖都由專門的對象表示,對象名通常叫Monitor。一樣,在很多語言中,信號量通常也有專門的對象名來表示,比如,Mutex,Semphore。信號量模型要比同步鎖模型復(fù)雜許多。一些系統(tǒng)中,信號量乃至能夠跨進程進行同步。另外一些信號量乃至還有計數(shù)功能,能夠操縱同時運行的線程數(shù)。咱們沒有必要考慮那么復(fù)雜的模型。所有那些復(fù)雜的模型,都是最大體的模型衍生出來的。只要把握

20、了最大體的信號量模型“等待/通知”模型,復(fù)雜模型也就迎刃而解了。咱們?nèi)允且訨ava語言為例。Java語言里面的同步鎖和信號量概念都超級模糊,沒有專門的對象名詞來表示同步鎖和信號量,只有兩個同步鎖相關(guān)的關(guān)鍵字volatile和synchronized。這種模糊盡管致使概念不清,但同時也幸免了Monitor、Mutex、Semphore等名詞帶來的各類誤解。咱們沒必要執(zhí)著于名詞之爭,能夠?qū)W⒂诿靼椎脤嶋H的運行原理。在Java語言里面,任何一個ObjectReference都能夠作為同步鎖。一樣的道理,任何一個ObjectReference也能夠作為信號量。Object對象的wait()方式確實是等

21、待通知,Object對象的notify()方式確實是發(fā)出通知。具體挪用方式為(1)等待某個信號量的通知publicstaticfinalObjectsignal=newObject();f1()synchronized(singal)/第一咱們要獲取那個信號量。那個信號量同時也是一個同步鎖/只有成功獲取了signal那個信號量兼同步鎖以后,咱們才可能進入這段代碼();/那個地址要舍棄信號量。本線程要進入signal信號量的待召(Waiting)隊列/可憐。辛辛苦苦爭取得手的信號量,就這么被舍棄了/等到通知以后,從待召(Waiting)隊列轉(zhuǎn)到就緒(Ready)隊列里面/轉(zhuǎn)到了就緒隊列中,離CP

22、LB心近了一步,就有機遇繼續(xù)執(zhí)行下面的代碼了。/仍然需要把signal同步鎖競爭得手,才能夠真正繼續(xù)執(zhí)行下面的代碼。命苦啊。需要注意的是,上述代碼中的()的意思。()很容易致使誤解。()的意思并非是說,signal開始wait,而是說,運行這段代碼的當(dāng)前線程開始wait那個signal對象,即進入signal對象的待召(Waiting)隊列。(2)發(fā)出某個信號量的通知f2()synchronized(singal)/第一,咱們一樣要獲取那個信號量。同時也是一個同步鎖。同時也是一個同步鎖。/只有成功獲取了signal那個信號量兼同步鎖以后,咱們才可能進入這段代碼();/那個地址,咱們通知sign

23、al的待召隊列中的某個線程。/若是某個線程等到了那個通知,那個線程就會轉(zhuǎn)到就緒隊列中/可是本線程仍然繼續(xù)擁有signal那個同步鎖,本線程仍然繼續(xù)執(zhí)行/嘿嘿,盡管本線程好心通知其他線程,/可是,本線程可沒有那么高風(fēng)亮節(jié),舍棄得手的同步鎖/本線程繼續(xù)執(zhí)行下面的代碼需要注意的是,()的意思。()并非是通知signal那個對象本身。而是通知正在等待signal信號量的其他線程。以上確實是Object的wait()和notify()的大體用法。事實上,wait()還能夠概念等待時刻,當(dāng)線程在某信號量的待召隊列中,等到足夠長的時刻,就會等無可等,無需再等,自己就從待召隊列轉(zhuǎn)移到就緒隊列中了。另外,還有一個notifyAll()方式,表示通知待召

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論