摘要:由于臨界區(qū)的存在,多線程之間的并發(fā)必須受到控制。對于非公平鎖來說,系統允許高優(yōu)先級的線程插隊。這樣有可能導致低優(yōu)先級線程產生饑餓。它要求所有線程都必須在有限步內完成,這樣不會引起饑餓問題。
由于臨界區(qū)的存在,多線程之間的并發(fā)必須受到控制。根據控制并發(fā)的策略,我們可以把并發(fā)的級別分為阻塞、無饑餓、無障礙、無鎖、無等待幾種。
阻塞一個線程是阻塞的,那么在其他線程釋放資源之前,當前線程無法繼續(xù)執(zhí)行。當我們使用synchronized關鍵字或者重入鎖時,我們得到的就是阻塞的線程。
synchronize關鍵字和重入鎖都試圖在執(zhí)行后續(xù)代碼前,得到臨界區(qū)的鎖,如果得不到,線程就會被掛起等待,直到占有了所需資源為止。
無饑餓(Starvation-Free)如果線程之間是有優(yōu)先級的,那么線程調度的時候總是會傾向于先滿足高優(yōu)先級的線程。也就是說,對于同一個資源的分配,是不公平的!圖1.7中顯示了非公平鎖與公平鎖兩種情況(五角星表示高優(yōu)先級線程)。對于非公平鎖來說,系統允許高優(yōu)先級的線程插隊。這樣有可能導致低優(yōu)先級線程產生饑餓。但如果鎖是公平的,按照先來后到的規(guī)則,那么饑餓就不會產生,不管新來的線程優(yōu)先級多高,要想獲得資源,就必須乖乖排隊,這樣所有的線程都有機會執(zhí)行。
無障礙(Obstruction-Free)無障礙是一種最弱的非阻塞調度。兩個線程如果無障礙地執(zhí)行,那么不會因為臨界區(qū)的問題導致一方被掛起。換言之,大家都可以大搖大擺地進入臨界區(qū)了。那么大家一起修改共享數據,把數據改壞了怎么辦呢?對于無障礙的線程來說,一旦檢測到這種情況,它就會立即對自己所做的修改進行回滾,確保數據安全。但如果沒有數據競爭發(fā)生,那么線程就可以順利完成自己的工作,走出臨界區(qū)。
如果說阻塞的控制方式是悲觀策略,也就是說,系統認為兩個線程之間很有可能發(fā)生不幸的沖突,因此以保護共享數據為第一優(yōu)先級,相對來說,非阻塞的調度就是一種樂觀的策略。它認為多個線程之間很有可能不會發(fā)生沖突,或者說這種概率不大。因此大家都應該無障礙地執(zhí)行,但是一旦檢測到沖突,就應該進行回滾。
從這個策略中也可以看到,無障礙的多線程程序并不一定能順暢運行。因為當臨界區(qū)中存在嚴重的沖突時,所有的線程可能都會不斷地回滾自己的操作,而沒有一個線程可以走出臨界區(qū)。這種情況會影響系統的正常執(zhí)行。所以,我們可能會非常希望在這一堆線程中,至少可以有一個線程能夠在有限的時間內完成自己的操作,而退出臨界區(qū)。至少這樣可以保證系統不會在臨界區(qū)中進行無限的等待。
一種可行的無障礙實現可以依賴一個"一致性標記"來實現。線程在操作之前,先讀取并保存這個標記,在操作完成后,再次讀取,檢查這個標記是否被更改過,如果兩者是一致的,則說明資源訪問沒有沖突。如果不一致,則說明資源可能在操作過程中與其他線程沖突,需要重試操作。而任何對資源有修改操作的線程,在修改數據前,都需要更新這個一致性標記,表示數據不再安全。
數據庫中樂觀鎖,應該比較熟悉,表中需要一個字段version(版本號),每次更新數據version+1,更新的時候將版本號作為條件進行更新,根據更新影響的行數判斷更新是否成功,偽代碼如下:
1.查詢數據,此時版本號為w_v
2.打開事務
3.做一些業(yè)務操作
4.update t set version = version+1 where id = 記錄id and version = w_v;//此行會返回影響的行數c
5.
if(c>0){ //提交事務 }else{ //回滾事務 }
多個線程更新同一條數據的時候,數據庫會對當前數據加鎖,同一時刻只有一個線程可以執(zhí)行更新語句。
無鎖(Lock-Free)無鎖的并行都是無障礙的。在無鎖的情況下,所有的線程都能嘗試對臨界區(qū)進行訪問,但不同的是,無鎖的并發(fā)保證必然有一個線程能夠在有限步內完成操作離開臨界區(qū)。
在無鎖的調用中,一個典型的特點是可能會包含一個無窮循環(huán)。在這個循環(huán)中,線程會不斷嘗試修改共享變量。如果沒有沖突,修改成功,那么程序退出,否則繼續(xù)嘗試修改。但無論如何,無鎖的并行總能保證有一個線程是可以勝出的,不至于全軍覆沒。至于臨界區(qū)中競爭失敗的線程,他們必須不斷重試,直到自己獲勝。如果運氣很不好,總是嘗試不成功,則會出現類似饑餓的先寫,線程會停止。
下面就是一段無鎖的示意代碼,如果修改不成功,那么循環(huán)永遠不會停止。
while(!atomicVar.compareAndSet(localVar, localVar+1)){ localVal = atomicVar.get(); }無等待
無鎖只要求有一個線程可以在有限步內完成操作,而無等待則在無鎖的基礎上更進一步擴展。它要求所有線程都必須在有限步內完成,這樣不會引起饑餓問題。如果限制這個步驟的上限,還可以進一步分解為有界無等待和線程數無關的無等待等幾種,他們之間的區(qū)別只是對循環(huán)次數的限制不同。
一種典型的無等待結果就是RCU(Read Copy Update)。它的基本思想是,對數據的讀可以不加控制。因此,所有的讀線程都是無等待的,它們既不會被鎖定等待也不會引起任何沖突。但在寫數據的時候,先獲取原始數據的副本,接著只修改副本數據(這就是為什么讀可以不加控制),修改完成后,在合適的時機回寫數據。
喜歡就關注我吧文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/75880.html
摘要:方法由兩個參數,表示期望的值,表示要給設置的新值。操作包含三個操作數內存位置預期原值和新值。如果處的值尚未同時更改,則操作成功。中就使用了這樣的操作。上面操作還有一點是將事務范圍縮小了,也提升了系統并發(fā)處理的性能。 這是java高并發(fā)系列第21篇文章。 本文主要內容 從網站計數器實現中一步步引出CAS操作 介紹java中的CAS及CAS可能存在的問題 悲觀鎖和樂觀鎖的一些介紹及數據庫...
摘要:有三種狀態(tài)運行關閉終止。類類,提供了一系列工廠方法用于創(chuàng)建線程池,返回的線程池都實現了接口。線程池的大小一旦達到最大值就會保持不變,在提交新任務,任務將會進入等待隊列中等待。此線程池支持定時以及周期性執(zhí)行任務的需求。 這是java高并發(fā)系列第19篇文章。 本文主要內容 介紹Executor框架相關內容 介紹Executor 介紹ExecutorService 介紹線程池ThreadP...
摘要:一旦等到期望的事件,線程就會再次進入運行狀態(tài)。表示結束狀態(tài),線程執(zhí)行完畢之后進入結束狀態(tài)。一個進程可以包括多個線程。 進程 進程(Process)是計算機中的程序關于某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位,是操作系統結構的基礎。程序是指令、數據及其組織形式的描述,進程是程序的實體。 進程具有的特征: 動態(tài)性:進程是程序的一次執(zhí)行過程,是臨時的,有生命期的,是動態(tài)...
摘要:我們需要先了解這些概念。在中,其表現在對于共享變量的某些操作,是不可分的,必須連續(xù)的完成。有序性有序性指的是程序按照代碼的先后順序執(zhí)行。 JMM(java內存模型),由于并發(fā)程序要比串行程序復雜很多,其中一個重要原因是并發(fā)程序中數據訪問一致性和安全性將會受到嚴重挑戰(zhàn)。如何保證一個線程可以看到正確的數據呢?這個問題看起來很白癡。對于串行程序來說,根本就是小菜一碟,如果你讀取一個變量,這個...
摘要:阿姆達爾定律定律是計算機科學中非常重要的定律。它定義了串行系統并行化后的加速比的計算公式和理論上線。需要從根本上修改程序的串行行為,提高系統內可并行化的模塊比重,在此基礎上,合理增加并行處理器數量,才能以最小的投入,得到最大的加速比。 有關為什么要使用并行程序的問題前面已經進行了簡單的探討??偟膩碚f,最重要的應該是處于兩個目的。 第一,為了獲得更好的性能; 第二,由于業(yè)務模型的需要,確...
閱讀 2474·2021-11-16 11:45
閱讀 2444·2021-10-11 10:59
閱讀 2251·2021-10-08 10:05
閱讀 3816·2021-09-23 11:30
閱讀 2370·2021-09-07 09:58
閱讀 790·2019-08-30 15:55
閱讀 773·2019-08-30 15:53
閱讀 1923·2019-08-29 17:00