版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
在JAVA中線程到底起到什么作用!這是javaeye上非常經(jīng)典的關(guān)于線程的帖子,寫的非常通俗易懂的,適合任何讀計算機(jī)的同學(xué).線程同步我們可以在計算機(jī)上運行各種計算機(jī)軟件程序。每一個運行的程序可能包括多個獨立運行的線程(Thread)。線程(Thread)是一份獨立運行的程序,有自己專用的運行棧。線程有可能和其他線程共享一些資源,比如,內(nèi)存,文件,數(shù)據(jù)庫等。當(dāng)多個線程同時讀寫同一份共享資源的時候,可能會引起沖突。這時候,我們需要引入線程“同步”機(jī)制,即各位線程之間要有個先來后到,不能一窩蜂擠上去搶作一團(tuán)。同步這個詞是從英文synchronize(使同時發(fā)生)翻譯過來的。我也不明白為什么要用這個很容易引起誤解的詞。既然大家都這么用,咱們也就只好這么將就。線程同步的真實意思和字面意思恰好相反。線程同步的真實意思,其實是“排隊”:幾個線程之間要排隊,一個一個對共享資源進(jìn)行操作,而不是同時進(jìn)行操作。因此,關(guān)于線程同步,需要牢牢記住的第一點是:線程同步就是線程排隊。同步就是排隊。線程同步的目的就是避免線程“同步”執(zhí)行。這可真是個無聊的繞口令。關(guān)于線程同步,需要牢牢記住的第二點是“共享”這兩個字。只有共享資源的讀寫訪問才需要同步。如果不是共享資源,那么就根本沒有同步的必要。關(guān)于線程同步,需要牢牢記住的第三點是,只有“變量”才需要同步訪問。如果共享的資源是固定不變的,那么就相當(dāng)于“常量”,線程同時讀取常量也不需要同步。至少一個線程修改共享資源,這樣的情況下,線程之間就需要同步。關(guān)于線程同步,需要牢牢記住的第四點是:多個線程訪問共享資源的代碼有可能是同一份代碼,也有可能是不同的代碼;無論是否執(zhí)行同一份代碼,只要這些線程的代碼訪問同一份可變的共享資源,這些線程之間就需要同步。為了加深理解,下面舉幾個例子有兩個采購員,他們的工作內(nèi)容是相同的,都是遵循如下的步驟:(1)到市場上去,尋找并購買有潛力的樣品。(2)回到公司,寫報告。這兩個人的工作內(nèi)容雖然一樣,他們都需要購買樣品,他們可能買到同樣種類的樣品,但是他們絕對不會購買到同一件樣品,他們之間沒有任何共享資源。所以,他們可以各自進(jìn)行自己的工作,互不干擾。這兩個采購員就相當(dāng)于兩個線程;兩個采購員遵循相同的工作步驟,相當(dāng)于這兩個線程執(zhí)行同一段代碼。下面給這兩個采購員增加一個工作步驟。采購員需要根據(jù)公司的“布告欄”上面公布的信息,安排自己的工作計劃。這兩個采購員有可能同時走到布告欄的前面,同時觀看布告欄上的信息。這一點問題都沒有。因為布告欄是只讀的,這兩個采購員誰都不會去修改布告欄上寫的信息下面增加一個角色。一個辦公室行政人員這個時候,也走到了布告欄前面,準(zhǔn)備修改布告欄上的信息。如果行政人員先到達(dá)布告欄,并且正在修改布告欄的內(nèi)容。兩個采購員這個時候,恰好也到了。這兩個采購員就必須等待行政人員完成修改之后,才能觀看修改后的信息。如果行政人員到達(dá)的時候,兩個采購員已經(jīng)在觀看布告欄了。那么行政人員需要等待兩個采購員把當(dāng)前信息記錄下來之后,才能夠?qū)懮闲碌男畔?。上述這兩種情況,行政人員和采購員對布告欄的訪問就需要進(jìn)行同步。因為其中一個線程(行政人員)修改了共享資源(布告欄)。而且我們可以看到,行政人員的工作流程和采購員的工作流程(執(zhí)行代碼)完全不同,但是由于他們訪問了同一份可變共享資源(布告欄,)所以他們之間需要同步。同步鎖前面講了為什么要線程同步,下面我們就來看如何才能線程同步。線程同步的基本實現(xiàn)思路還是比較容易理解的。我們可以給共享資源加一把鎖,這把鎖只有一把鑰匙。哪個線程獲取了這把鑰匙,才有權(quán)利訪問該共享資源。生活中,我們也可能會遇到這樣的例子。一些超市的外面提供了一些自動儲物箱。每個儲物箱都有一把鎖,一把鑰匙。人們可以使用那些帶有鑰匙的儲物箱,把東西放到儲物箱里面,把儲物箱鎖上,然后把鑰匙拿走。這樣,該儲物箱就被鎖住了,其他人不能再訪問這個儲物箱。(當(dāng)然,真實的儲物箱鑰匙是可以被人拿走復(fù)制的,所以不要把貴重物品放在超市的儲物箱里面。于是很多超市都采用了電子密碼鎖。)線程同步鎖這個模型看起來很直觀。但是,還有一個嚴(yán)峻的問題沒有解決,這個同步鎖應(yīng)該加在哪里?當(dāng)然是加在共享資源上了。反應(yīng)快的讀者一定會搶先回答。沒錯,如果可能,我們當(dāng)然盡量把同步鎖加在共享資源上。一些比較完善的共享資源,比如,文件系統(tǒng),數(shù)據(jù)庫系統(tǒng)等,自身都提供了比較完善的同步鎖機(jī)制。我們不用另外給這些資源加鎖,這些資源自己就有鎖。但是,大部分情況下,我們在代碼中訪問的共享資源都是比較簡單的共享對象。這些對象里面沒有地方讓我們加鎖。讀者可能會提出建議:為什么不在每一個對象內(nèi)部都增加一個新的區(qū)域,專門用來加鎖呢?這種設(shè)計理論上當(dāng)然也是可行的。問題在于,線程同步的情況并不是很普遍。如果因為這小概率事件,在所有對象內(nèi)部都開辟一塊鎖空間,將會帶來極大的空間浪費。得不償失。于是,現(xiàn)代的編程語言的設(shè)計思路都是把同步鎖加在代碼段上。確切的說,是把同步鎖加在“訪問共享資源的代碼段”上。這一點一定要記住,同步鎖是加在代碼段上的。同步鎖加在代碼段上,就很好地解決了上述的空間浪費問題。但是卻增加了模型的復(fù)雜度,也增加了我們的理解難度?,F(xiàn)在我們就來仔細(xì)分析“同步鎖加在代碼段上”的線程同步模型。首先,我們已經(jīng)解決了同步鎖加在哪里的問題。我們已經(jīng)確定,同步鎖不是加在共享資源上,而是加在訪問共享資源的代碼段上。其次,我們要解決的問題是,我們應(yīng)該在代碼段上加什么樣的鎖。這個問題是重點中的重點。這是我們尤其要注意的問題:訪問同一份共享資源的不同代碼段,應(yīng)該加上同一個同步鎖;如果加的是不同的同步鎖,那么根本就起不到同步的作用,沒有任何意義。這就是說,同步鎖本身也一定是多個線程之間的共享對象。Java語言的synchronized關(guān)鍵字為了加深理解,舉幾個代碼段同步的例子。不同語言的同步鎖模型都是一樣的。只是表達(dá)方式有些不同。這里我們以當(dāng)前最流行的Java語言為例。Java語言里面用synchronized關(guān)鍵字給代碼段加鎖。整個語法形式表現(xiàn)為synchronized(同步鎖){//訪問共享資源,需要同步的代碼段}這里尤其要注意的就是,同步鎖本身一定要是共享的對象。…f1()(Objectlockl=newObject();//產(chǎn)生一個同步鎖synchronized(lock1){//代碼段A//訪問共享資源resourcel//需要同步}}上面這段代碼沒有任何意義。因為那個同步鎖是在函數(shù)體內(nèi)部產(chǎn)生的。每個線程調(diào)用這段代碼的時候,都會產(chǎn)生一個新的同步鎖。那么多個線程之間,使用的是不同的同步鎖。根本達(dá)不到同步的目的。同步代碼一定要寫成如下的形式,才有意義。publicstaticfinalObjectlockl=newObject();…f1()(synchronized(lock1)(//lock1是公用同步鎖//代碼段A//訪問共享資源resource1//需要同步}你不一定要把同步鎖聲明為static或者public,但是你一定要保證相關(guān)的同步代碼之間,一定要使用同一個同步鎖。講到這里,你一定會好奇,這個同步鎖到底是個什么東西。為什么隨便聲明一個Object對象,就可以作為同步鎖?在Java里面,同步鎖的概念就是這樣的。任何一個ObjectReference都可以作為同步鎖。我們可以把ObjectReference理解為對象在內(nèi)存分配系統(tǒng)中的內(nèi)存地址。因此,要保證同步代碼段之間使用的是同一個同步鎖,我們就要保證這些同步代碼段的synchronized關(guān)鍵字使用的是同一個ObjectReference,同一個內(nèi)存地址。這也是為什么我在前面的代碼中聲明lock1的時候,使用了final關(guān)鍵字,這就是為了保證lock1的ObjectReference在整個系統(tǒng)運行過程中都保持不變。一些求知欲強(qiáng)的讀者可能想要繼續(xù)深入了解synchronzied(同步鎖)的實際運行機(jī)制。Java虛擬機(jī)規(guī)范中(你可以在google用“JVMSpec”等關(guān)鍵字進(jìn)行搜索),有對synchronized關(guān)鍵字的詳細(xì)解釋synchronized會編譯成monitorenter,…monitorexit之類的指令對。Monitor就是實際上的同步鎖。每一個ObjectReference在概念上都對應(yīng)一個monitor0這些實現(xiàn)細(xì)節(jié)問題,并不是理解同步鎖模型的關(guān)鍵。我們繼續(xù)看幾個例子,加深對同步鎖模型的理解。publicstaticfinalObjectlock1=newObject();…f1()(synchronized(lock1)(//lock1是公用同步鎖//代碼段A//訪問共享資源resource1//需要同步}}…f2()(synchronized(lock1)(//lock1是公用同步鎖//代碼段B//訪問共享資源resource1//需要同步}}上述的代碼中,代碼段A和代碼段B就是同步的。因為它們使用的是同一個同步鎖lockl。如果有10個線程同時執(zhí)行代碼段A,同時還有20個線程同時執(zhí)行代碼段B,那么這30個線程之間都是要進(jìn)行同步的。這30個線程都要競爭一個同步鎖lockl。同一時刻,只有一個線程能夠獲得lockl的所有權(quán),只有一個線程可以執(zhí)行代碼段A或者代碼段B。其他競爭失敗的線程只能暫停運行,進(jìn)入到該同步鎖的就緒(Ready)隊列。每一個同步鎖下面都掛了幾個線程隊列,包括就緒(Ready)隊列,待召(Waiting)隊列等。比如,lockl對應(yīng)的就緒隊列就可以叫做lockl-readyqueue。每個隊列里面都可能有多個暫停運行的線程。注意,競爭同步鎖失敗的線程進(jìn)入的是該同步鎖的就緒(Ready)隊列,而不是后面要講述的待召隊列(WaitingQueue,也可以翻譯為等待隊列)。就緒隊列里面的線程總是時刻準(zhǔn)備著競爭同步鎖,時刻準(zhǔn)備著運行。而待召隊列里面的線程則只能一直等待,直到等到某個信號的通知之后,才能夠轉(zhuǎn)移到就緒隊列中,準(zhǔn)備運行。成功獲取同步鎖的線程,執(zhí)行完同步代碼段之后,會釋放同步鎖。該同步鎖的就緒隊列中的其他線程就繼續(xù)下一輪同步鎖的競爭。成功者就可以繼續(xù)運行,失敗者還是要乖乖地待在就緒隊列中。因此,線程同步是非常耗費資源的一種操作。我們要盡量控制線程同步的代碼段范圍。同步的代碼段范圍越小越好。我們用一個名詞“同步粒度”來表示同步代碼段的范圍。同步粒度在Java語言里面,我們可以直接把synchronized關(guān)鍵字直接加在函數(shù)的定義上。比如。...synchronized...f1()(//fl代碼段}這段代碼就等價于…f1()(synchronized(this)(//同步鎖就是對象本身//fl代碼段}}同樣的原則適用于靜態(tài)(static)函數(shù)比如。...staticsynchronized...f1()(//fl代碼段}這段代碼就等價于...static...f1()(synchronized(Class.forName(?))(//同步鎖是類定義本//fl代碼段}}但是,我們要盡量避免這種直接把synchronized加在函數(shù)定義上的偷懶做法。因為我們要控制同步粒度。同步的代碼段越小越好。synchronized控制的范圍越小越好。我們不僅要在縮小同步代碼段的長度上下功夫,我們同時還要注意細(xì)分同步鎖。比如,下面的代碼publicstaticfinalObjectlockl=newObject();…f1()(synchronized(lock1)(//lock1是公用同步鎖//代碼段A//訪問共享資源resource1//需要同步}}…f2()(synchronized(lock1)(//lock1是公用同步鎖//代碼段B//訪問共享資源resource】//需要同步}}...f3()(synchronized(lock1)(//lock1是公用同步鎖//代碼段C//訪問共享資源resource2//需要同步}}…f4()(synchronized(lock1)(//lock1是公用同步鎖//代碼段D//訪問共享資源resource2//需要同步}}上述的4段同步代碼,使用同一個同步鎖lock1。所有調(diào)用4段代碼中任何一段代碼的線程,都需要競爭同一個同步鎖lock1。我們仔細(xì)分析一下,發(fā)現(xiàn)這是沒有必要的。因為f1()的代碼段A和f2()的代碼段B訪問的共享資源是resource],f3()的代碼段C和f4()的代碼段D訪問的共享資源是resource2,它們沒有必要都競爭同一個同步鎖lock1。我們可以增加一個同步鎖lock2。f3()和f4()的代碼可以修改為:publicstaticfinalObjectlock2=newObject();…f3()(synchronized(lock2)(//lock2是公用同步鎖//代碼段C//訪問共享資源resource2//需要同步}}f4()(synchronized(lock2)(//lock2是公用同步鎖//代碼段D//訪問共享資源resource2//需要同步}}這樣,f1()和f2()就會競爭lockl,而f3()和f4()就會競爭lock2。這樣,分開來分別競爭兩個鎖,就可以大大較少同步鎖競爭的概率,從而減少系統(tǒng)的開銷。信號量同步鎖模型只是最簡單的同步模型。同一時刻,只有一個線程能夠運行同步代碼。有的時候,我們希望處理更加復(fù)雜的同步模型,比如生產(chǎn)者/消費者模型、讀寫同步模型等。這種情況下,同步鎖模型就不夠用了。我們需要一個新的模型。這就是我們要講述的信號量模型。信號量模型的工作方式如下:線程在運行的過程中,可以主動停下來,等待某個信號量的通知;這時候,該線程就進(jìn)入到該信號量的待召(Waiting)隊列當(dāng)中;等到通知之后,再繼續(xù)運行。很多語言里面,同步鎖都由專門的對象表示,對象名通常叫Monitor。同樣,在很多語言中,信號量通常也有專門的對象名來表示,比如,Mutex,Semphore。信號量模型要比同步鎖模型復(fù)雜許多。一些系統(tǒng)中,信號量甚至可以跨進(jìn)程進(jìn)行同步。另外一些信號量甚至還有計數(shù)功能,能夠控制同時運行的線程數(shù)。我們沒有必要考慮那么復(fù)雜的模型。所有那些復(fù)雜的模型,都是最基本的模型衍生出來的。只要掌握了最基本的信號量模型一一“等待/通知”模型,復(fù)雜模型也就迎刃而解了。我們還是以Java語言為例。Java語言里面的同步鎖和信號量概念都非常模糊,沒有專門的對象名詞來表示同步鎖和信號量,只有兩個同步鎖相關(guān)的關(guān)鍵字一一volatile和synchronized。這種模糊雖然導(dǎo)致概念不清,但同時也避免了Monitor、Mutex、Semphore等名詞帶來的種種誤解。我們不必執(zhí)著于名詞之爭,可以專注于理解實際的運行原理。在Java語言里面,任何一個ObjectReference都可以作為同步鎖。同樣的道理,任何一個ObjectReference也可以作為信號量。Object對象的wait()方法就是等待通知,Object對象的notify()方法就是發(fā)出通知。具體調(diào)用方法為(1)等待某個信號量的通知publicstaticfinalObjectsignal=newObject();…f1()(synchronized(singal){//首先我們要獲取這個信號量。這個信號量同時也是一個同步鎖//只有成功獲取了signal這個信號量兼同步鎖之后,我們才可能進(jìn)入這段代碼signal.wait();//這里要放棄信號量。本線程要進(jìn)入signal信號量的待召(Waiting)隊列//可憐。辛辛苦苦爭取到手的信號量,就這么被放棄了//等到通知之后,從待召(Waiting)隊列轉(zhuǎn)到就緒(Ready)隊列里面//轉(zhuǎn)到了就緒隊列中,離CPU核心近了一步,就有機(jī)會繼續(xù)執(zhí)行下面的代碼了。//仍然需要把signal同步鎖競爭到手,才能夠真正繼續(xù)執(zhí)行下面的代碼。命苦啊。…}}需要注意的是,上述代碼中的signal.wait()的意思。signal.wait()彳艮容易導(dǎo)致誤解。signal.wait()的意思并不是說,signal開始wait,而是說,運行這段代碼的當(dāng)前線程開始wait這個signal對象,即進(jìn)入signal對象的待召(Waiting)隊列。(2)發(fā)出某個信號量的通知…f2()(synchronized(singal){//首先,我們同樣要獲取這個信號量。同時也是一個同步鎖。//只有成功獲取了signal這個信號量兼同步鎖之后,我們才可能進(jìn)入這段代碼signal.notify();//這里,我們通知signal的待召隊列中的某個線程?!ㄈ绻硞€線程等到了這個通知,那個線程就會轉(zhuǎn)到就緒隊列中//但是本線程仍然繼續(xù)擁有signal這個同步鎖,本線程仍然繼續(xù)執(zhí)行//嘿嘿,雖然本線程好心通知其他線程,//但是,本線程可沒有那么高風(fēng)亮節(jié),放棄到手的同步鎖//本線程繼續(xù)執(zhí)行下面的代碼…}}需要注意的是,signal.notify()的意思。signal.notify()并不是通知signal這個對象本身。而是通知正在等待signal信號量的其他線程。以上就是Object的wait()和notify()的基本用法。實際上,wait()還可以定義等待時間,當(dāng)線程在某信號量的待召隊列中,等到足夠長的時間,就會等無可等,無需再等,自己就從待召隊列轉(zhuǎn)移到就
溫馨提示
- 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)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 專利代理居間合同樣本
- 物業(yè)管理委托合同
- 家庭室內(nèi)外裝修合同書
- 多模式跨境電子商務(wù)解決方案策劃與設(shè)計全案指南
- 研發(fā)項目管理作業(yè)指導(dǎo)書
- 生物技術(shù)與實驗室技能作業(yè)指導(dǎo)書
- 電線電纜購銷合同
- 2025年天津年貨運從業(yè)資格證考試從業(yè)從業(yè)資格資格題庫及答案
- 2025年烏魯木齊貨運從業(yè)資格考試題目大全
- 小學(xué)青島版一年級數(shù)學(xué)上冊口算練習(xí)題總匯
- 《配電網(wǎng)設(shè)施可靠性評價指標(biāo)導(dǎo)則》
- 2024年國家電網(wǎng)招聘之通信類題庫附參考答案(考試直接用)
- ## 外事領(lǐng)域意識形態(tài)工作預(yù)案
- CJJ 169-2012城鎮(zhèn)道路路面設(shè)計規(guī)范
- 第八單元金屬和金屬材料單元復(fù)習(xí)題-2023-2024學(xué)年九年級化學(xué)人教版下冊
- 鋼鐵是怎樣煉成的保爾成長史
- 精神科護(hù)理技能5.3出走行為的防范與護(hù)理
- 煤礦機(jī)電運輸培訓(xùn)課件
- 采購管理學(xué)教學(xué)課件
- 《供應(yīng)商質(zhì)量會議》課件
- 江蘇省科技企業(yè)孵化器孵化能力評價研究的中期報告
評論
0/150
提交評論