成全视频在线观看免费高清,国产亚洲欧洲精品,成年人午夜免费视频,久久机热综合久久国产

posix線程同步機制

瀏覽:87 發(fā)布日期:2023-08-06 00:00:00 投稿人:佚名投稿

1、java多線程,對象鎖是什么概念?

java線程:

1.線程中一些基本術語和概念

1.1線程的幾個狀態(tài)
初始化狀態(tài)
就緒狀態(tài)
運行狀態(tài)
阻塞狀態(tài)
終止狀態(tài)
1.2 Daemon線程
Daemon線程區(qū)別一般線程之處是:主程序一旦結束,Daemon線程就會結束。
1.3鎖的定義
為了協(xié)調(diào)多個并發(fā)運行的線程使用共享資源才引入了鎖的概念。
1.4死鎖
任何多線程應用程序都有死鎖風險。當一組線程中的每一個都在等待一個只
有該組中另一個線程才能引起的事件時,我們就說這組線程死鎖了。換一個說法
就是一組線程中的每一個成員都在等待別的成員占有的資源時候,就可以說這組
線程進入了死鎖。死鎖的最簡單情形是:線程 A 持有對象 X 的獨占鎖,并且
在等待對象 Y 的鎖,而線程 B 持有對象 Y 的獨占鎖,卻在等待對象 X 的鎖。
除非有某種方法來打破對鎖的等待(Java 鎖定不支持這種方法),否則死鎖的線
程將永遠等下去。

1.5.Java對象關于鎖的幾個方法
1.5.1 wait方法
wait方法是java根對象Object含有的方法,表示等待獲取某個鎖。在wait方法進入前,會釋放相應的鎖,在wait方法返回時,會再次獲得某個鎖。
如果wait()方法不帶有參數(shù),那只有當持有該對象鎖的其他線程調(diào)用了notify或者notifyAll方法,才有可能再次獲得該對象的鎖。
如果wait()方法帶有參數(shù),比如:wait(10),那當持有該對象鎖的其他線程調(diào)用了notify或者notifyAll方法,或者指定時間已經(jīng)過去了,才有可能再次獲得該對象的鎖。
參考 thread.lock.SleepAndWait
1.5.2 notify/notifyAll方法
這里我就不再說明了。哈哈,偷點懶。
1.5.3 yield方法
yield()會自動放棄CPU,有時比sleep更能提升性能。
1.6鎖對象(實例方法的鎖)
在同步代碼塊中使用鎖的時候,擔當鎖的對象可以是這個代碼所在對象本身或者一個單獨的對象擔任,但是一定要確保鎖對象不能為空。如果對一個null對象加鎖,會產(chǎn)生異常的。原則上不要選擇一個可能在鎖的作用域中會改變值的實例變量作為鎖對象。
鎖對象,一種是對象自己擔任,一種是定義一個普通的對象作為private property來擔任,另外一種是建立一個新的類,然后用該類的實例來擔任。
參考 :
thread.lock.UseSelfAsLock,使用對象自己做鎖對象
thread.lock.UseObjAsLock 使用一個實例對象作鎖對象
thread.lock.UseAFinalObjAsLock使用常量對象作為一個鎖對象
1.7類鎖
實例方法存在同步的問題,同樣,類方法也存在需要同步的情形。一般類方法的類鎖是一個static object來擔任的。當然也可以采用類本身的類對象來作為類鎖。
一個類的實例方法可以獲得該類實例鎖,還可以嘗試去訪問類方法,包含類同步方法,去獲得類鎖。
一個類的類方法,可以嘗試獲得類鎖,但是不可以嘗試直接獲得實例鎖。需要先生成一個實例,然后在申請獲得這個實例的實例鎖。
參考
thread.lock.UseStaticObjAsStaticLock 使用類的屬性對象作為類鎖。
thread.lock.UseClassAsStaticLock使用類的類對象作為類鎖

1.8.線程安全方法與線程不安全方法
如果一個對象的所有的public方法都是同步方法,也就是說是public方法是線程安全的,那該對象的private方法,在不考慮繼承的情況下,可以設置為不是線程安全的方法。
參考 thread.lock.SynMethrodAndNotSynMethrod

1.9類鎖和實例鎖混合使用
在實例方法中混合使用類鎖和實例鎖;可以根據(jù)前面說的那樣使用實例鎖和類鎖。
在類方法中混合使用類鎖和實例鎖,可以根據(jù)前面說的那樣使用類鎖,為了使用實例鎖,先得生成一個實例,然后實例鎖。
參考 thread.lock.StaticLockAndObjLock
1.10鎖的粒度問題。
為了解決對象鎖的粒度過粗,會導死鎖出現(xiàn)的可能性加大,鎖的粒度過細,會程序開發(fā)維護的工作加大。對于鎖的粒度大小,這完全要根據(jù)實際開發(fā)需要來考慮,很難有一個統(tǒng)一的標準。

1.11.讀寫鎖
一個讀寫鎖支持多個線程同時訪問一個對象,但是在同一時刻只有一個線程可以修改此對象,并且在訪問進行時不能修改。
有2種調(diào)度策略,一種是讀鎖優(yōu)先,另外就是寫鎖優(yōu)先。
參考 thread.lock.ReadWriteLock
1.12 volatile
在Java中設置變量值的操作,除了long和double類型的變量外都是原子操作,也就是說,對于變量值的簡單讀寫操作沒有必要進行同步。這在JVM 1.2之前,Java的內(nèi)存模型實現(xiàn)總是從主存讀取變量,是不需要進行特別的注意的。而隨著JVM的成熟和優(yōu)化,現(xiàn)在在多線程環(huán)境下volatile關鍵字的使用變得非常重要。在當前的Java內(nèi)存模型下,線程可以把變量保存在本地內(nèi)存(比如機器的寄存器)中,而不是直接在主存中進行讀寫。這就可能造成一個線程在主存中修改了一個變量的值,而另外一個線程還繼續(xù)使用它在寄存器中的變量值的拷貝,造成數(shù)據(jù)的不一致。要解決這個問題,只需要像在本程序中的這樣,把該變量聲明為volatile(不穩(wěn)定的)即可,這就指示JVM,這個變量是不穩(wěn)定的,每次使用它都到主存中進行讀取。一般說來,多任務環(huán)境下各任務間共享的標志都應該加volatile修飾。

2.線程之間的通訊
在其他語言中,線程之間可以通過消息隊列,共享內(nèi)存,管道等方式來實現(xiàn)
線程之間的通訊,但是java中可以不采用這樣方式,關注的是線程之間的同步。
只要保證相關方法運行的線程安全,信息共享是自然就可以顯現(xiàn)了。
2.1屏障
屏障就是這樣的一個等待點: 一組線程在這一點被同步,這些線程合并各自的結果或者運行到整體任務的下一階段。
參考:
thread.lock. BarrierUseExample
thread.lock.Barrier
2.2.鎖工具類
提供對線程鎖的獲取,釋放功能。展示了鎖的獲取釋放過程??梢宰鳛橐粋€工具類來使用。
參考:thread.lock. BusyFlag

2.3.條件變量
條件變量是POSIX線程模型提供的一種同步類型,和java中的等待通知機制類似。
雖然java中已經(jīng)有了等待通知機制,但是為了減少在notify/notifyAll方法中
線程調(diào)度的開銷,把一些不需要激活的線程屏蔽出去,引入了條件變量。
Java中2個(多個)條件變量可以是同一個互斥體(鎖對象)。
參考:thread.lock.CondVar 條件變量類
常見的應用情形:
一個鎖控制多個信號通道(例如:多個變量),雖然可以采用簡單java等待通知機制,但是線程調(diào)度效率不高,而且線程可讀性也不是太好,這時候可以采用創(chuàng)建一個鎖對象(BusyFlag實例),同時使用這個BusyFlag實例來創(chuàng)建多個條件變量(CondVar 實例)。
經(jīng)常使用到CondVar類的地方是緩沖區(qū)管理,比如:管道操作之類的。先創(chuàng)建一個BusyFlag實例,然后創(chuàng)建CondVar 實例,用這個條件變量描述緩沖區(qū)是否為空,另外創(chuàng)建CondVar 實例作條件變量述緩沖區(qū)是否滿。
現(xiàn)實中,馬路的紅綠燈,就可以采用條件變量來描述。

3. Java線程調(diào)度
3.1 Java優(yōu)先級
java的優(yōu)先級別共有10種,加上虛擬機自己使用的優(yōu)先級別=0這種,總共11種。
大多數(shù)情況來說,java線程的優(yōu)先級設置越高(最高=10),那線程越優(yōu)先運行。
3.2. 綠色線程
線程運行在虛擬機內(nèi),操作系統(tǒng)根本不知道這類線程的存在。
線程是由虛擬機調(diào)度的。
3.3 本地線程
線程是由運行虛擬機的操作系統(tǒng)完成的。
3.4 Windows本地線程
操作系統(tǒng),完全能夠看得到虛擬機內(nèi)的每一個線程,同時虛擬機的線程和操作系統(tǒng)的線程是一一對應的。Java的線程調(diào)度室由操作系統(tǒng)底層線程決定的。
在win32平臺下,windows線程只有6個優(yōu)先級別。和java線程優(yōu)先級別對應如下:
Java線程優(yōu)先級 Windows 95/nt/2000線程優(yōu)先級
0 THREAD_ PRIORITY_IDLE
1(Thread.MIN_PRIORITY) THREAD_ PRIORITY_LOWEST
2 THREAD_ PRIORITY_LOWEST
3 THREAD_ PRIORITY_BELOW_NORMAL
4 THREAD_ PRIORITY_BELOW_NORMAL
5 (Thread.NORM_PRIORITY) THREAD_ PRIORITY _NORMAL
6 THREAD_ PRIORITY _ABOVE_NORMAL
7 THREAD_ PRIORITY _ABOVE_NORMA
8 THREAD_ PRIORITY _HIGHEST
9 THREAD_ PRIORITY _HIGHEST
10 (Thread.MAX_PRIORITY) THREAD_ PRIORITY _CRITICAL

3.5線程優(yōu)先級倒置與繼承
如果一個線程持有鎖(假設該線程名字=ThreadA,優(yōu)先級別=5),另外一個線程(假設該線程名字=ThreadB,優(yōu)先級別=7),現(xiàn)在該線程(ThreadA)處于運行狀態(tài),但是線程ThreadB申請需要持有ThreadA所獲得的鎖,這時候,為了避免死鎖,線程A提高其運行的優(yōu)先級別(提高到ThreadB的優(yōu)先級別=7),而線程ThreadB為了等待獲得鎖,降低線程優(yōu)先級別(降低到ThreadA原來的優(yōu)先級別=5).
上述的這種情況,對于ThreadA,繼承了ThreadB的優(yōu)先級別,這成為優(yōu)先級別的繼承;對于ThreadB暫時降低了優(yōu)先級別,成為優(yōu)先級別的倒置。
當然,一旦線程ThreadA持有的鎖釋放了,其優(yōu)先級別也會回到原來的優(yōu)先級別(優(yōu)先級別=5)。線程ThreadB獲得了相應的鎖,那優(yōu)先級別也會恢復到與原來的值(優(yōu)先級別=7)。

3.6循環(huán)調(diào)度
具有同樣優(yōu)先級的線程相互搶占成為循環(huán)調(diào)度。

4.線程池
創(chuàng)建一個線程也是需要一定代價的,為了降低這個代價,采用了和普通對象池的思想建立線程池,以供系統(tǒng)使用。
線程消耗包括內(nèi)存和其它系統(tǒng)資源在內(nèi)的大量資源。除了 Thread 對象所需的內(nèi)存之外,每個線程都需要兩個可能很大的執(zhí)行調(diào)用堆棧。除此以外,JVM 可能會為每個 Java 線程創(chuàng)建一個本機線程,這些本機線程將消耗額外的系統(tǒng)資源。最后,雖然線程之間切換的調(diào)度開銷很小,但如果有很多線程,環(huán)境切換也可能嚴重地影響程序的性能。
使用線程池的方式是,先建立對象池,然后申請使用線程,程序線程運行,運行完畢,把線程返回線程池。
使用線程池的風險:同步錯誤和死鎖,與池有關的死鎖、資源不足和線程泄漏。
大家有空可以研究一下tomcat的線程池實現(xiàn)原理思想。
實際上是tomcat已經(jīng)在從線程池的使用線程時候加上了事件處理機制。
個人認為,線程池之類的實現(xiàn),一般不要自己實現(xiàn),因為自己實現(xiàn)主要是穩(wěn)定性等方面可能作的不夠好。
可以參考 apache的jakarta-tomcat-5.5.6的相關代碼,具體是:
jakarta-tomcat-connectors\util\java\org\apache\tomcat\util\threads的相關代碼

5工作隊列
使用工作隊列的好處是不象直接使用線程池那樣,當線城池中沒有線程可以使用的時
候,使用者需要處于等待狀態(tài),不能進行其他任務的處理。
工作隊列的工作原理是:
采用后臺線程處理方式,客戶端把任務提交給工作隊列,工作隊列有一組內(nèi)部可以工作線程,這些工作線程從工作隊列中取出任務運行,一個任務完成后,就從隊列獲取下一個任務進行處理。當工作隊列中沒有任務可以處理時候,工作線程就處于等待狀態(tài),直到獲得新的任務時候,才進行新的處理。 對象鎖。同一時間只保證 一個線程訪問方法或變量。
在Java語言中,通過被關鍵字synchronized修飾的方法或synchronized語句塊實現(xiàn)對代碼的同步
包含在synchronized方法或語句塊中的代碼稱為被同步的代碼(Synchronized Code)
當線程訪問被同步的代碼時,必須首先競爭代碼所屬的類的【對象上的鎖】,否則線程將等待(阻塞),直到鎖被釋放.
------------------------------------------------------------------------------------
我們上課用的PPT 上截取的例子如下:
同步語句(synchronized statements)的一般形式如下:
synchronized(<鎖對象引用>){
…被同步的代碼…
}
同步語句提供了比被同步的方法更細粒度(finer granularity)的鎖機制,增強了類的并發(fā)性.
------------------------------------------------------------------------------------
比如 售票 票數(shù)是一定的,售票的人很多。如何保證 票數(shù)及時更新 這就需要 同步方法了。 鎖就相當于是獨占該資源,讓別的對象不能訪問到該資源。形象的說就是鎖住了就是只屬于你的東西,別人就看不到,更用不到了。
sleep貌似不需要獲取鎖的,而wait是需要獲取鎖的..
不知道我的答案是否能幫助你!

2、線程的線程的同步

線程的同步是Java多線程編程的難點,往往開發(fā)者搞不清楚什么是競爭資源、什么時候需要考慮同步,怎么同步等等問題,當然,這些問題沒有很明確的答案,但有些原則問題需要考慮,是否有競爭資源被同時改動的問題?對于同步,在具體的Java代碼中需要完成以下兩個操作:把競爭訪問的資源標識為private;同步哪些修改變量的代碼,使用synchronized關鍵字同步方法或代碼。當然這不是唯一控制并發(fā)安全的途徑。synchronized關鍵字使用說明synchronized只能標記非抽象的方法,不能標識成員變量。為了演示同步方法的使用,構建了一個信用卡賬戶,起初信用額為100w,然后模擬透支、存款等多個操作。顯然銀行賬戶User對象是個競爭資源,而多個并發(fā)操作的是賬戶方法oper(int x),當然應該在此方法上加上同步,并將賬戶的余額設為私有變量,禁止直接訪問。
工作原理
線程是進程中的實體,一個進程可以擁有多個線程,一個線程必須有一個父進程。線程不擁有系統(tǒng)資源,只有運行必須的一些數(shù)據(jù)結構;它與父進程的其它線程共享該進程所擁有的全部資源。線程可以創(chuàng)建和撤消線程,從而實現(xiàn)程序的并發(fā)執(zhí)行。一般,線程具有就緒、阻塞和運行三種基本狀態(tài)。
在多中央處理器的系統(tǒng)里,不同線程可以同時在不同的中央處理器上運行,甚至當它們屬于同一個進程時也是如此。大多數(shù)支持多處理器的操作系統(tǒng)都提供編程接口來讓進程可以控制自己的線程與各處理器之間的關聯(lián)度(affinity)。
有時候,線程也稱作輕量級進程。就象進程一樣,線程在程序中是獨立的、并發(fā)的執(zhí)行路徑,每個線程有它自己的堆棧、自己的程序計數(shù)器和自己的局部變量。但是,與分隔的進程相比,進程中的線程之間的隔離程度要小。它們共享內(nèi)存、文件句柄和其它每個進程應有的狀態(tài)。
進程可以支持多個線程,它們看似同時執(zhí)行,但互相之間并不同步。一個進程中的多個線程共享相同的內(nèi)存地址空間,這就意味著它們可以訪問相同的變量和對象,而且它們從同一堆中分配對象。盡管這讓線程之間共享信息變得更容易,但您必須小心,確保它們不會妨礙同一進程里的其它線程。
Java 線程工具和 API看似簡單。但是,編寫有效使用線程的復雜程序并不十分容易。因為有多個線程共存在相同的內(nèi)存空間中并共享相同的變量,所以您必須小心,確保您的線程不會互相干擾。
線程屬性
為了正確有效地使用線程,必須理解線程的各個方面并了解Java 實時系統(tǒng)。必須知道如何提供線程體、線程的生命周期、實時系統(tǒng)如 何調(diào)度線程、線程組、什么是幽靈線程(Demo nThread)。
線程體
所有的操作都發(fā)生在線程體中,在Java中線程體是從Thread類繼承的run()方法,或?qū)崿F(xiàn)Runnable接口的類中的run()方法。當線程產(chǎn)生并初始化后,實時系統(tǒng)調(diào)用它的run()方法。run()方法內(nèi)的代碼實現(xiàn)所產(chǎn)生線程的行為,它是線程的主要部分。
線程狀態(tài)
附圖表示了線程在它的生命周期內(nèi)的任何時刻所能處的狀態(tài)以及引起狀態(tài)改變的方法。這圖并不是完整的有限狀態(tài)圖,但基本概括了線程中比較感興趣和普遍的方面。以下討論有關線程生命周期以此為據(jù)。
●新線程態(tài)(New Thread)
產(chǎn)生一個Thread對象就生成一個新線程。當線程處于新線程狀態(tài)時,僅僅是一個空線程對象,它還沒有分配到系統(tǒng)資源。因此只能啟動或終止它。任何其他操作都會引發(fā)異常。例如,一個線程調(diào)用了new方法之后,并在調(diào)用start方法之前的處于新線程狀態(tài),可以調(diào)用start和stop方法。
●可運行態(tài)(Runnable)
start()方法產(chǎn)生運行線程所必須的資源,調(diào)度線程執(zhí)行,并且調(diào)用線程的run()方法。在這時線程處于可運行態(tài)。該狀態(tài)不稱為運行態(tài)是因為這時的線程并不總是一直占用處理機。特別是對于只有一個處理機的PC而言,任何時刻只能有一個處于可運行態(tài)的線程占用處理 機。Java通過調(diào)度來實現(xiàn)多線程對處理機的共享。注意,如果線程處于Runnable狀態(tài),它也有可能不在運行,這是因為還有優(yōu)先級和調(diào)度問題。
●阻塞/非運行態(tài)(Not Runnable)
當以下事件發(fā)生時,線程進入非運行態(tài)。

①suspend()方法被調(diào)用;
②sleep()方法被調(diào)用;
③線程使用wait()來等待條件變量;
④線程處于I/O請求的等待。
●死亡態(tài)(Dead)
當run()方法返回,或別的線程調(diào)用stop()方法,線程進入死亡態(tài)。通常Applet使用它的stop()方法來終止它產(chǎn)生的所有線程。
線程的本操作:
派生:線程在進程內(nèi)派生出來,它即可由進程派生,也可由線程派生。
阻塞(Block):如果一個線程在執(zhí)行過程中需要等待某個事件發(fā)生,則被阻塞。
激活(unblock):如果阻塞線程的事件發(fā)生,則該線程被激活并進入就緒隊列。
調(diào)度(schedule):選擇一個就緒線程進入執(zhí)行狀態(tài)。
結束(Finish):如果一個線程執(zhí)行結束,它的寄存器上下文以及堆棧內(nèi)容等將被釋放。
圖2 線程的狀態(tài)與操作
線程的另一個執(zhí)行特性是同步。線程中所使用的同步控制機制與進程中所使用的同步控制機制相同。
線程優(yōu)先級
雖然我們說線程是并發(fā)運行的。然而事實常常并非如此。正如前面談到的,當系統(tǒng)中只有一個CPU時,以某種順序在單CPU情況下執(zhí)行多線程被稱為調(diào)度(scheduling)。Java采用的是一種簡單、固定的調(diào)度法,即固定優(yōu)先級調(diào)度。這種算法是根據(jù)處于可運行態(tài)線程的相對優(yōu)先級來實行調(diào)度。當線程產(chǎn)生時,它繼承原線程的優(yōu)先級。在需要時可對優(yōu)先級進行修改。在任何時刻,如果有多條線程等待運行,系統(tǒng)選擇優(yōu)先級最高的可運行線程運行。只有當它停止、自動放棄、或由于某種原因成為非運行態(tài)低優(yōu)先級的線程才能運行。如果兩個線程具有相同的優(yōu)先級,它們將被交替地運行?!ava實時系統(tǒng)的線程調(diào)度算法還是強制性的,在任何時刻,如果一個比其他線程優(yōu)先級都高的線程的狀態(tài)變?yōu)榭蛇\行態(tài),實時系統(tǒng)將選擇該線程來運行。一個應用程序可以通過使用線程中的方法setPriority(int),來設置線程的優(yōu)先級大小。
有線程進入了就緒狀態(tài),需要有線程調(diào)度程序來決定何時執(zhí)行,根據(jù)優(yōu)先級來調(diào)度。
線程中的join()可以用來邀請其他線程先執(zhí)行(示例代碼如下):
packageorg.thread.test;publicclassJoin01implementsRunnable{publicstaticvoidmain(String[]args){for(inti=0;i<20;i++){if(i==5){Join01j=newJoin01();Threadt=newThread(j);t.setName(被邀請先執(zhí)行的線程.);t.start();try{//邀請這個線程,先執(zhí)行t.join();}catch(InterruptedExceptione){e.printStackTrace();}}System.out.println(沒被邀請的線程。+(i+1));}}publicvoidrun(){for(inti=0;i<10;i++){System.out.println(Thread.currentThread().getName()+(i+1));}}}
yield()告訴系統(tǒng)把自己的CPU時間讓掉,讓其他線程或者自己運行,示例代碼如下:
packageorg.thread.test;
publicclassYield01
{
publicstaticvoidmain(String[]args)
{
YieldFirstyf=newYieldFirst();
YieldSecondys=newYieldSecond();
YieldThirdyt=newYieldThird();
yf.start();ys.start();yt.start();
}
}
classYieldFirstextendsThread
{
@Overridepublicvoidrun()
{
for(inti=0;i<10;i++)
{
System.out.println(第一個線程第+(i+1)+次運行.);//讓當前線程暫停yield();
}
}
}
classYieldSecondextendsThread
{
@Overridepublicvoidrun()
{
for(inti=0;i<10;i++)
{
System.out.println(第二個線程第+(i+1)+次運行.);//讓當前線程暫停yield();
<a href=mailto:}}}classYieldThirdextendsThread{@Overridepublicvoidrun(){for(inti=0;i}
}
}
classYieldThirdextendsThread
{
@Overridepublicvoidrun(){for(inti=0;i<10;i++)
{
System.out.println(第三個線程第+(i+1)+次運行.);//讓當前線程暫停yield();
}
}
幽靈線程
任何一個Java線程都能成為幽靈線程。它是作為運行于同一個進程內(nèi)的對象和線程的服務提供者。例如,HotJava瀏覽器有一個稱為 后臺圖片閱讀器的幽靈線程,它為需要圖片的對象和線程從文件系統(tǒng)或網(wǎng)絡讀入圖片?!∮撵`線程是應用中典型的獨立線程。它為同一應用中的其他對象和線程提供服務。幽靈線程的run()方法一般都是無限循環(huán),等待服務請求。
線程組
每個Java線程都是某個線程組的成員。線程組提供一種機制,使得多個線程集于一個對象內(nèi),能對它們實行整體操作。譬如,你能用一個方法調(diào)用來啟動或掛起組內(nèi)的所有線程。Java線程組由ThreadGroup類實現(xiàn)。
當線程產(chǎn)生時,可以指定線程組或由實時系統(tǒng)將其放入某個缺省的線程組內(nèi)。線程只能屬于一個線程組,并且當線程產(chǎn)生后不能改變它所屬的線程組。
多線程
對于多線程的好處這就不多說了。但是,它同樣也帶來了某些新的麻煩。只要在設計程序時特別小心留意,克服這些麻煩并不算太困難。在生成線程時必須將線程放在指定的線程組,也可以放在缺省的線程組中,缺省的就是生成該線程的線程所在的線程組。一旦一個線程加入了某個線程組,不能被移出這個組。
同步線程
許多線程在執(zhí)行中必須考慮與其他線程之間共享數(shù)據(jù)或協(xié)調(diào)執(zhí)行狀態(tài)。這就需要同步機制。在Java中每個對象都有一把鎖與之對應。但Java不提供單獨的lock和unlock操作。它由高層的結構隱式實現(xiàn),來保證操作的對應。(然而,我們注意到Java虛擬機提供單獨的monito renter和monitorexit指令來實現(xiàn)lock和
unlock操作。) synchronized語句計算一個對象引用,試圖對該對象完成鎖操作,并且在完成鎖操作前停止處理。當鎖操作完成synchronized語句體得到執(zhí)行。當語句體執(zhí)行完畢(無論正?;虍惓#?,解鎖操作自動完成。作為面向?qū)ο蟮恼Z言,synchronized經(jīng)常與方法連用。一種比較好的辦法是,如果某個變量由一個線程賦值并由別的線程引用或賦值,那么所有對該變量的訪問都必須在某個synchromized語句或synchronized方法內(nèi)。
現(xiàn)在假設一種情況:線程1與線程2都要訪問某個數(shù)據(jù)區(qū),并且要求線程1的訪問先于線程2,則這時僅用synchronized是不能解決問題的。這在Unix或Windows NT中可用Simaphore來實現(xiàn)。而Java并不提供。在Java中提供的是wait()和notify()機制。使用如下:
synchronizedmethod_1(/*……*/){//calledbythread1.//accessdataareaavailable=true;notify();}synchronizedmethod_2(/*……*/){//calledbythread2.while(!available)try{wait();//waitfornotify().}catch(InterruptedExceptione){}//accessdataarea}
其中available是類成員變量,置初值為false。
如果在method-2中檢查available為假,則調(diào)用wait()。wait()的作用是使線程2進入非運行態(tài),并且解鎖。在這種情況下,method-1可以被線程1調(diào)用。當執(zhí)行notify()后。線程2由非運行態(tài)轉(zhuǎn)變?yōu)榭蛇\行態(tài)。當method-1調(diào)用返回后。線程2可重新對該對象加鎖,加鎖成功后執(zhí)行wait()返回后的指令。這種機制也能適用于其他更復雜的情況。
死鎖
如果程序中有幾個競爭資源的并發(fā)線程,那么保證均衡是很重要的。系統(tǒng)均衡是指每個線程在執(zhí)行過程中都能充分訪問有限的資源。系統(tǒng)中沒有餓死和死鎖的線程。Java并不提供對死鎖的檢測機制。對大多數(shù)的Java程序員來說防止死鎖是一種較好的選擇。最簡單的防止死鎖的方法是對競爭的資源引入序號,如果一個線程需要幾個資源,那么它必須先得到小序號的資源,再申請大序號的資源。
優(yōu)化
Java的多線程安全是基于Lock機制實現(xiàn)的,而Lock的性能往往不如人意。原因是,monitorenter與monitorexit這兩個控制多線程同步的bytecode原語,是JVM依賴操作系統(tǒng)互斥(mutex)來實現(xiàn)的。而互斥是一種會導致線程掛起,并在較短的時間內(nèi)又需要重新調(diào)度回原線程的,較為消耗資源的操作。所以需要進行對線程進行優(yōu)化,提高效率。
輕量級鎖
輕量級鎖(Lightweight Locking)是從Java6開始引入的概念,本意是為了減少多線程進入互斥的幾率,并不是要替代互斥。它利用了CPU原語Compare-And-Swap(CAS,匯編指令CMPXCHG),嘗試在進入互斥前,進行補救。下面將詳細介紹JVM如何利用CAS,實現(xiàn)輕量級鎖。
Java Object Model中定義,Object Header是一個2字(1 word = 4 byte)長度的存儲區(qū)域。第一個字長度的區(qū)域用來標記同步,GC以及hash code等,官方稱之為 mark word。第二個字長度的區(qū)域是指向到對象的Class。在2個word中,mark word是輕量級鎖實現(xiàn)的關鍵,其結構見右表。
從表中可以看到,state為lightweight locked的那行即為輕量級鎖標記。bitfieds名為指向lock record的指針,這里的lock record,其實是一塊分配在線程堆棧上的空間區(qū)域。用于CAS前,拷貝object上的mark word。第三項是重量級鎖標記。后面的狀態(tài)單詞很有趣,inflated,譯為膨脹,在這里意思其實是鎖已升級到OS-level。一般我們只關注第二和第三項即可。lock,unlock與mark word之間的聯(lián)系如右圖所示。在圖中,提到了拷貝object mark word,由于脫離了原始mark word,官方將它冠以displaced前綴,即displaced mark word(置換標記字)。這個displaced mark word是整個輕量級鎖實現(xiàn)的關鍵,在CAS中的compare就需要用它作為條件。
在拷貝完object mark word之后,JVM做了一步交換指針的操作,即流程中第一個橙色矩形框內(nèi)容所述。將object mark word里的輕量級鎖指針指向lock record所在的stack指針,作用是讓其他線程知道,該object monitor已被占用。lock record里的owner指針指向object mark word的作用是為了在接下里的運行過程中,識別哪個對象被鎖住了。
最后一步unlock中,我們發(fā)現(xiàn),JVM同樣使用了CAS來驗證object mark word在持有鎖到釋放鎖之間,有無被其他線程訪問。如果其他線程在持有鎖這段時間里,嘗試獲取過鎖,則可能自身被掛起,而mark word的重量級鎖指針也會被相應修改。此時,unlock后就需要喚醒被掛起的線程。
偏向鎖
Java偏向鎖(Biased Locking)是Java 6引入的一項多線程優(yōu)化。它通過消除資源無競爭情況下的同步原語,進一步提高了程序的運行性能。它與輕量級鎖的區(qū)別在于,輕量級鎖是通過CAS來避免進入開銷較大的互斥操作,而偏向鎖是在無競爭場景下完全消除同步,連CAS也不執(zhí)行(CAS本身仍舊是一種操作系統(tǒng)同步原語,始終要在JVM與OS之間來回,有一定的開銷)。所謂的無競爭場景,就是單線程訪問帶同步的資源或方法。
偏向鎖,顧名思義,它會偏向于第一個訪問鎖的線程,如果在接下來的運行過程中,該鎖沒有被其他的線程訪問,則持有偏向鎖的線程將永遠不需要觸發(fā)同步。如果在運行過程中,遇到了其他線程搶占鎖,則持有偏向鎖的線程會被掛起,JVM會嘗試消除它身上的偏向鎖,將鎖恢復到標準的輕量級鎖。(偏向鎖只能在單線程下起作用)。
偏向模式和非偏向模式,在mark word表中,主要體現(xiàn)在thread ID字段是否為空。
掛起持有偏向鎖的線程,這步操作類似GC的pause,但不同之處是,它只掛起持有偏向鎖的線程(非當前線程)。
在搶占模式的橙色區(qū)域說明中有提到,指向當前堆棧中最近的一個lock record(在輕量級鎖中,lock record是進入鎖前會在stack上創(chuàng)建的一份內(nèi)存空間)。這里提到的最近的一個lock record,其實就是當前鎖所在的stack frame上分配的lock record。整個步驟是從偏向鎖恢復到輕量級鎖的過程。
偏向鎖也會帶來額外開銷。在JDK6中,偏向鎖是默認啟用的。它提高了單線程訪問同步資源的性能。
但試想一下,如果你的同步資源或代碼一直都是多線程訪問的,那么消除偏向鎖這一步驟對你來說就是多余的。事實上,消除偏向鎖的開銷還是蠻大的。所以在你非常熟悉自己的代碼前提下,大可禁用偏向鎖 -XX:-UseBiasedLocking。
分類
線程有兩個基本類型:
用戶級線程:管理過程全部由用戶程序完成,操作系統(tǒng)內(nèi)核心只對進程進行管理。
系統(tǒng)級線程(核心級線程):由操作系統(tǒng)內(nèi)核進行管理。操作系統(tǒng)內(nèi)核給應用程序提供相應的系統(tǒng)調(diào)用和應用程序接口API,以使用戶程序可以創(chuàng)建、執(zhí)行、撤消線程。
舉例UNIX International 線程
UNIX International 線程的頭文件是<thread.h> ,僅適用于Sun Solaris操作系統(tǒng)。所以UNIX International線程也常被俗稱為Solaris線程。
1.創(chuàng)建線程
intthr_create(void*stack_base,size_tstack_size,void*(*start_routine)(void*),void*arg,longflags,thread_t*new_thr);
2.等待線程
intthr_join(thread_twait_for,thread_t*dead,void**status);
3.掛起線程
intthr_suspend(thread_tthr);
4.繼續(xù)線程
intthr_continue(thread_tthr);
5.退出線程
voidthr_exit(void*status);
6.返回當前線程的線程標識符
thread_tthr_self(void);POSIX線程
POSIX線程(Pthreads)的頭文件是<pthread.h>,適用于類Unix操作系統(tǒng)。Windows操作系統(tǒng)并沒有對POSIX線程提供原生的支持庫。不過Win32的POSIX線程庫的一些實現(xiàn)也還是有的,例如pthreads-w32 。
1.創(chuàng)建線程
intpthread_create(pthread_t*thread,constpthread_attr_t*attr,void*(*start_routine)(void*),void*arg);
2.等待線程
intpthread_join(pthread_tthread,void**retval);
3.退出線程
voidpthread_exit(void*retval);
4.返回當前線程的線程標識符
pthread_tpthread_self(void);
5.線程取消
intpthread_cancel(pthread_tthread);Win32線程
Win32線程的頭文件是<Windows.h>,適用于Windows操作系統(tǒng)。
1.創(chuàng)建線程
HANDLEWINAPICreateThread(LPSECURITY_ATTRIBUTESlpThreadAttributes,SIZE_TdwStackSize,LPTHREAD_START_ROUTINElpStartAddress,LPVOIDlpParameter,DWORDdwCreationFlags,LPDWORDlpThreadId);
2.結束本線程
VOIDWINAPIExitThread(DWORDdwExitCode);
3.掛起指定的線程
DWORDWINAPISuspendThread(HANDLEhThread);
4.恢復指定線程運行
DWORDWINAPIResumeThread(HANDLEhThread);
5.等待線程運行完畢
DWORDWINAPIWaitForSingleObject(HANDLEhHandle,DWORDdwMilliseconds);
6.返回當前線程的線程標識符
DWORDWINAPIGetCurrentThreadId(void);
7.返回當前線程的線程句柄
HANDLEWINAPIGetCurrentThread(void);C++ 11 線程
C++ 11 線程的頭文件是<thread>。 創(chuàng)建線程
std::thread::thread(Function&& f, Args&&... args); 等待線程結束
std::thread::join(); 脫離線程控制
std::thread::detach(); 交換線程
std::thread::swap( thread& other ); C 11 線程
C11線程的頭文件是<threads.h>。
C11線程僅僅是個“建議標準”,也就是說100%遵守C11標準的C編譯器是可以不支持C11線程的。根據(jù)C11標準的規(guī)定,只要編譯器預定義了__STDC_NO_THREADS__宏,就可以沒有<threads.h>頭文件,自然也就也沒有下列函數(shù)。
1.創(chuàng)建線程
intthrd_create(thrd_t*thr,thrd_start_tfunc,void*arg);
2.結束本線程
_Noreturnvoidthrd_exit(intres);
3.等待線程運行完畢
intthrd_join(thrd_tthr,int*res);
4.返回當前線程的線程標識符
thrd_tthrd_current();Java線程
1)最簡單的情況是,Thread/Runnable的run()方法運行完畢,自行終止。
2)對于更復雜的情況,比如有循環(huán),則可以增加終止標記變量和任務終止的檢查點。
3)最常見的情況,也是為了解決阻塞不能執(zhí)行檢查點的問題,用中斷來結束線程,但中斷只是請求,并不能完全保證線程被終止,需要執(zhí)行線程協(xié)同處理。
4)IO阻塞和等鎖情況下需要通過特殊方式進行處理。
5)使用Future類的cancel()方法調(diào)用。
6)調(diào)用線程池執(zhí)行器的shutdown()和shutdownNow()方法。
7)守護線程會在非守護線程都結束時自動終止。
8)Thread的stop()方法,但已不推薦使用。
線程的組成
1)一組代表處理器狀態(tài)的CPU寄存器中的內(nèi)容
2)兩個棧,一個用于當線程在內(nèi)核模式下執(zhí)行的時候,另一個用于線程在用戶模式下執(zhí)行的時候
3)一個被稱為線程局部存儲器(TLS,thread-local storage)的私有儲存區(qū)域,各個子系統(tǒng)、運行庫和DLL都會用到該儲存區(qū)域
4)一個被稱為線程ID(thread ID,線程標識符)的唯一標識符(在內(nèi)部也被稱為客戶ID——進程ID和線程ID是在同一個名字空間中生產(chǎn)的,所以它們永遠 不會重疊)
5)有時候線程也有它們自己的安全環(huán)境,如果多線程服務器應用程序要模仿其客戶的安全環(huán)境,則往往可以利用線程的安全環(huán)境

3、求教:線程同步和進程同步有什么區(qū)別

線程同步:多線程編程中,解決共享資源沖突的問題
進程同步:多進程編程中,解決共享資源沖突的問題

但是部分同學對線程同步和進程同步研究得不夠深入,比如互斥鎖和條件變量能不能同時用于線程同步和進程同步,本質(zhì)上有什么區(qū)別。

首先我們知道,linux下每個進程都有自己的獨立進程空間,假設A進程和B進程各有一個互斥鎖,這個鎖放在進程的全局靜態(tài)區(qū),那么AB進程都是無法感知對方的互斥鎖的。

互斥鎖和條件變量出自Posix.1線程標準,它們總是可以用來同步一個進程內(nèi)的各個線程的。如果一個互斥鎖或者條件變量存放在多個進程共享的某個內(nèi)存區(qū)中,那么Posix還允許它用在這些進程間的同步。

看到這里,是不是發(fā)現(xiàn)點了什么,線程同步和進程同步的本質(zhì)區(qū)別在于鎖放在哪,放在私有的進程空間還是放在多進程共享的空間,并且看鎖是否具備進程共享的屬性, 進程至少包括一個主線程,還有工作線程
狹隘的講:線程通信就是進程范圍內(nèi)主線程與工作線程 或者 工作線程之間的通信

進程通信,是進程A(可以理解為主線程) 與 進程B(可以理解為主線程)之間的通信

4、linux 下進程間的同步機制有哪些

linux下進程間同步的機制有以下三種:
信號量
記錄鎖(文件鎖)
共享內(nèi)存中的mutex
效率上 共享內(nèi)存mutex > 信號量 > 記錄鎖
posix 提供了新的信號量 - 有名信號量,既可以使用在進程間同步也可以作為線程間同步的手段。效率比共享內(nèi)存mutex要好一些

5、Linux 多線程編程(二)2019-08-10

三種專門用于線程同步的機制:POSIX信號量,互斥量和條件變量.

在Linux上信號量API有兩組,一組是System V IPC信號量,即PV操作,另外就是POSIX信號量,POSIX信號量的名字都是以sem_開頭.

phshared參數(shù)指定信號量的類型,若其值為0,就表示這個信號量是當前進程的局部信號量,否則該信號量可以在多個進程之間共享.value值指定信號量的初始值,一般與下面的sem_wait函數(shù)相對應.

其中比較重要的函數(shù)sem_wait函數(shù)會以原子操作的方式將信號量的值減一,如果信號量的值為零,則sem_wait將會阻塞,信號量的值可以在sem_init函數(shù)中的value初始化;sem_trywait函數(shù)是sem_wait的非阻塞版本;sem_post函數(shù)將以原子的操作對信號量加一,當信號量的值大于0時,其他正在調(diào)用sem_wait等待信號量的線程將被喚醒.
這些函數(shù)成功時返回0,失敗則返回-1并設置errno.

生產(chǎn)者消費者模型:
生產(chǎn)者對應一個信號量:sem_t producer;
消費者對應一個信號量:sem_t customer;
sem_init(&producer,2)----生產(chǎn)者擁有資源,可以工作;
sem_init(&customer,0)----消費者沒有資源,阻塞;

在訪問公共資源前對互斥量設置(加鎖),確保同一時間只有一個線程訪問數(shù)據(jù),在訪問完成后再釋放(解鎖)互斥量.
互斥鎖的運行方式:串行訪問共享資源;
信號量的運行方式:并行訪問共享資源;
互斥量用pthread_mutex_t數(shù)據(jù)類型表示,在使用互斥量之前,必須使用pthread_mutex_init函數(shù)對它進行初始化,注意,使用完畢后需調(diào)用pthread_mutex_destroy.

pthread_mutex_init用于初始化互斥鎖,mutexattr用于指定互斥鎖的屬性,若為NULL,則表示默認屬性。除了用這個函數(shù)初始化互斥所外,還可以用如下方式初始化:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER。
pthread_mutex_destroy用于銷毀互斥鎖,以釋放占用的內(nèi)核資源,銷毀一個已經(jīng)加鎖的互斥鎖將導致不可預期的后果。

pthread_mutex_lock以原子操作給一個互斥鎖加鎖。如果目標互斥鎖已經(jīng)被加鎖,則pthread_mutex_lock則被阻塞,直到該互斥鎖占有者把它給解鎖.
pthread_mutex_trylock和pthread_mutex_lock類似,不過它始終立即返回,而不論被操作的互斥鎖是否加鎖,是pthread_mutex_lock的非阻塞版本.當目標互斥鎖未被加鎖時,pthread_mutex_trylock進行加鎖操作;否則將返回EBUSY錯誤碼。注意:這里討論的pthread_mutex_lock和pthread_mutex_trylock是針對普通鎖而言的,對于其他類型的鎖,這兩個加鎖函數(shù)會有不同的行為.
pthread_mutex_unlock以原子操作方式給一個互斥鎖進行解鎖操作。如果此時有其他線程正在等待這個互斥鎖,則這些線程中的一個將獲得它.


三個打印機輪流打印:

輸出結果:

如果說互斥鎖是用于同步線程對共享數(shù)據(jù)的訪問的話,那么條件變量就是用于在線程之間同步共享數(shù)據(jù)的值.條件變量提供了一種線程之間通信的機制:當某個共享數(shù)據(jù)達到某個值時,喚醒等待這個共享數(shù)據(jù)的線程.
條件變量會在條件不滿足的情況下阻塞線程.且條件變量和互斥量一起使用,允許線程以無競爭的方式等待特定的條件發(fā)生.

其中pthread_cond_broadcast函數(shù)以廣播的形式喚醒所有等待目標條件變量的線程,pthread_cond_signal函數(shù)用于喚醒一個等待目標條件變量線程.但有時候我們可能需要喚醒一個固定的線程,可以通過間接的方法實現(xiàn):定義一個能夠唯一標識目標線程的全局變量,在喚醒等待條件變量的線程前先設置該變量為目標線程,然后采用廣播的方式喚醒所有等待的線程,這些線程被喚醒之后都檢查該變量以判斷是否是自己.

采用條件變量+互斥鎖實現(xiàn)生產(chǎn)者消費者模型:

運行結果:

阻塞隊列+生產(chǎn)者消費者

運行結果:

轉(zhuǎn)載請帶上網(wǎng)址:http://yadikedp.com/posjifour/268965.html

版權聲明:本文內(nèi)容由互聯(lián)網(wǎng)用戶自發(fā)貢獻,該文觀點僅代表作者本人。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如發(fā)現(xiàn)本站有涉嫌抄襲侵權/違法違規(guī)的內(nèi)容, 請發(fā)送郵件至 babsan@163.com 舉報,一經(jīng)查實,本站將立刻刪除。
聯(lián)系我們
訂購聯(lián)系:小莉
微信聯(lián)系方式
地址:深圳市寶安區(qū)固戍聯(lián)誠發(fā)產(chǎn)業(yè)園木星大廈

公司地址:深圳市寶安區(qū)固戍聯(lián)誠發(fā)產(chǎn)業(yè)園木星大廈

舉報投訴 免責申明 版權申明 廣告服務 投稿須知 技術支持:第一POS網(wǎng) Copyright@2008-2030 深圳市慧聯(lián)實業(yè)有限公司 備案號:粵ICP備18141915號

  • <bdo id="ogrsz"></bdo>
      1. <pre id="ogrsz"><abbr id="ogrsz"></abbr></pre>
        <p id="ogrsz"></p>
        <bdo id="ogrsz"></bdo>
        主站蜘蛛池模板: 高淳县| 名山县| 筠连县| 苏尼特右旗| 贵南县| 城口县| 武宣县| 太仆寺旗| 阿城市| 益阳市| 洪江市| 永康市| 辽阳县| 宁夏| 锡林浩特市| 浦东新区| 德钦县| 湖北省| 新宾| 五大连池市| 西安市| 柳河县| 肇源县| 栾川县| 浦北县| 东乡族自治县| 濮阳县| 无为县| 平舆县| 泰宁县| 榆社县| 汕尾市| 慈溪市| 若羌县| 南雄市| 浪卡子县| 汉沽区| 颍上县| 铅山县| 微山县| 诸城市|