溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

volatile變量能保證線程安全性嗎?為什么?

發布時間:2020-07-10 13:30:22 來源:網絡 閱讀:951 作者:wx5d6cccb1cb158 欄目:編程語言
  1. volatile是什么?

在談及線程安全時,常會說到一個變量——volatile。在《Java并發編程實戰》一書中是這么定義volatile的——“Java語言提供了一種稍弱的同步機制,即volatile變量,用來確保將變量的更新操作通知到其他線程”。這句話說明了兩點:①volatile變量是一種同步機制;②volatile能夠確??梢娦?。這兩點和我們探討“volatile變量是否能夠保證線程安全性”息息相關。

  1. volatile變量能確保線程安全性嗎?為什么?

什么是同步機制?在并發程序設計中,各進程對公共變量的訪問必須加以制約,這種制約稱為同步。也就是說,同步機制即為對共享資源的一種制約。那么問題來了:volatile這種“稍弱的同步機制”是怎么制約各個進程對共享資源的訪問的呢?答案就在“volatile能夠確??梢娦浴敝?。

2.1 可見性

volatile能夠保證字段的可見性:volatile變量,用來確保將變量的更新操作通知到其他線程。volatile變量不會被緩存在寄存器或者對其他處理器不可見的地方,因此在讀取volatile類型的變量時總會返回最新寫入的值。

可見性和“線程如何對變量進行操作(取值、賦值等)”有關系:

我們要先明確一個定律:線程對變量的所有操作(取值、賦值等)都必須在工作內存(各線程獨立擁有)中進行,而不能直接讀寫內存中的變量,各工作內存間也不能相互訪問。對于volatile變量來說,由于它特殊的操作順序性規定,看起來如同操作主內存一般,但實際上 volatile變量也是遵循這一定律的。

關于主存與工作內存之間具體的交互協議(即一個變量如何從主存拷貝到工作內存、如何從工作內存同步到主存等實現細節),Java內存模型中定義了以下八種操作來完成:

lock:(鎖定),unlock(解鎖),read(讀取),load(載入),use(試用), assign(賦值),store(存儲),write(寫入)。

volatile 對這八種操作有著兩個特殊的限定,正因為有這些限定才讓volatile修飾的變量有可見性以及可以禁止指令重排序 :

① use動作之前必須要有read和load動作, 這三個動作必須是連續出現的?!颈硎荆好看喂ぷ鲀却嬉褂胿olatile變量之前必須去主存中拿取最新的volatile變量】

② assign動作之后必須跟著store和write動作,這三個動作必須是連續出現的?!颈硎? 每次工作內存改變了volatile變量的值,就必須把該值寫回到主存中】

有以上兩條規則就能保證每個線程每次去拿volatile變量的時候,那個變量肯定是最新的, 其實也就相當于好多個線程用的是同一個內存,無工作內存和主存之分。而操作沒有用volatile修飾的變量則不能保證每次都能獲取到最新的變量值。

2.2 所以volatile究竟能否保證線程安全性?

不能。

通過2.1,我們已經很明確在多線程環境下,一個線程修改了用volatile修飾的變量后,其他線程能夠立刻讀取到該變量的最新值。但是,volatile并不能保證各個線程是串行去訪問同一變量的,在機器是多核的情況下,兩個或多個線程同時對同一共享變量做修改,依舊會出現線程安全問題。例如:機器是多核的情況下,i == 0,兩個線程同時操作 i++,最終的結果就有可能出現錯誤的結果“ i == 1”。

所以:volatile不能保證線程安全性,因為要保證線程安全性就得保證是以串行形式來訪問操作共享資源的,而volatile做不到這點。

2.3 通過代碼來驗證“即使變量用了volatile來修飾,依舊會出現線程安全問題”volatile變量能保證線程安全性嗎?為什么?

volatile變量能保證線程安全性嗎?為什么?

測試結果:(1)出現了大量的重復數字; (2)最后還輸出了 “-1”;==》說明變量即使用volatile修飾了但依舊出現了線程安全問題。

代碼解析:
出現問題(1)的原因:線程存在“先檢查后執行”的競態條件??赡苡袃蓚€線程同時擁有CPU的執行權(機器是雙核的),它們判斷到做“if (ticket > 0)”,并同時做“ticket--”操作。
出現問題(2)的原因:
①當ticket==1時,兩個或多個線程同時通過了“if (ticket > 0)”的判斷,并進入了判斷框中去執行代碼;
②然后它們執行到“Thread.sleep(100);”就睡了;
③睡醒后總有一個線程會先搶到cup的執行權,然后執行“ticket--”操作,并將最新的ticket數值推送告知到每個線程;
④此時那些在判斷框中的其他的線程并不會再次做“if (ticket > 0)”的判斷,而是直接拿最新的ticket并做“ticket--”操作。
就算線程在“ticket--”之前每次都做“if (ticket > 0)”的判斷,也依舊會有線程安全問題,因為又可能出現①那種同時通過判斷的狀態。

總結:volatile只能確??梢娦院头乐棺侄沃嘏判颍ǚ乐棺侄沃嘏判虮疚闹袥]做深入討論),不能保證線程安全性。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女