volatile為什麼不能保證原子性的思考
1、故事的起因:
前幾天有人問過關於volatile關鍵字的一些問題, 想起了自己剛接觸這個關鍵字時候存在的疑惑,趕上今天有時間就整理了一些自己的想法。
首先,需要明確的一點是,volatile關鍵字修飾的變數可以保證該變數操作的有序性和可見性, 但是保證不了操作的原子性。
關於volatile關鍵字修飾的變數在確保變數操作的有序性和可見性的介紹其他文章已經有很詳細的介紹了,也比較通俗易懂,這裡針對這兩點就不再贅述了。
但是關於volatile關鍵字為什麼不能保證操作的原子性的問題剛開始我感到比較迷惑(雖然也查了一些文章,但是還是不能理解,可能是因為我比較愚鈍,O(∩_∩)O哈哈~),經過幾番腦神經搏鬥終於找到了一個能說服自己的解釋,但是不知道理解的是否準確。故而,在這裡寫下這些文字,一方面是想給同樣跟我一樣有這樣困惑的小夥伴提供一個思路,另一方面也是希望各位大神關於我這個理解是否存在偏差給出一些指點^_^。
2、問題的闡述
網上就volatile關鍵字的原子操作的介紹中大多用的是對int型別的變數進行自增操作的例子介紹的, 那我們也索性就用這個例子來繼續闡述吧。
private volatile int i = 0;
i++;
上邊的程式碼中的變數i是被volatile修飾的,他在下邊做i++操作的時候在多執行緒環境中並不安全,不安全的原因就是i++這個操作是非原子性的操作。
以下從“引用開始”標誌到“引用結束”標誌中的闡述是我對比較多的網上其他文章對i++操作不安全的解釋的理解
/*******************引用開始****************************/
如果在起比較多的執行緒,比如起了500條執行緒併發地去執行i++這個操作 最後的結果i是小於500的,解釋是:
i++操作可以被拆分為三步:
1,執行緒讀取i的值
2、i進行自增計算
3、重新整理回i的值
假設某一時刻i=5,此時有兩個執行緒同時從主存中讀取了i的值,那麼此時兩個執行緒儲存的i的值都是5, 此時A執行緒對i進行了自增計算,然後B也對i進行自增計算,此時兩條執行緒最後重新整理回主存的i的值都是6(本來兩條執行緒計算完應當是7)所以說volatile保證不了原子性。
/*******************引用結束**********************/
看完以上的解釋我的不解之處在於,既然i是被volatile修飾的變數,那麼對於i的操作應該是執行緒之間是可見的啊,就算A.,B兩個執行緒都同時讀到i的值是5,但是如果A執行緒執行完i的操作以後應該會把B執行緒讀到的i的值置為無效並強制B重新讀入i的新值也就是6然後才會進行自增操作才對啊。(這是我當時所存在的疑惑O(∩_∩)O哈哈~)
3、問題的自我解釋
上邊的問題困擾了我有一段時間,而且我從網上搜索的文章的解釋都大同小異(可能是我搜索的姿勢不對^_^)導致我始終困在那個圈裡。
有一天我坐在回家的車上又想到了這個問題,忽然把這個問題想通了(也可能是自己在騙自己0.0)(ps:由此看來,做開發需要多回家O(∩_∩)O哈哈~)
首先我覺得對i++這個操作需要更細的拆分
1,執行緒讀取i
2,i++ > i = i + 1 > (1) temp = i + 1, i = temp
當i=5的時候A,B兩個執行緒同時讀入了i的值, 然後A執行緒執行了 temp = i + 1的操作, 要注意,此時的i的值還沒有變化,然後B執行緒也執行了 temp = i + 1的操作,注意,此時A,B兩個執行緒儲存的 i的值都是5,temp的值都是6, 然後A執行緒執行了 i = 6(temp)的操作,此時i的值會立即重新整理到主存並通知其他執行緒儲存的i值失效, 此時B執行緒需要重新讀取i的值那麼此時B執行緒儲存的i就是6,同時B執行緒儲存的temp還仍然是6, 然後B執行緒執行i=6(temp),所以導致了計算結果比預期少了1。
以上是我個人自己的理解,僅供曾經也被同樣問題困惑過的小夥伴參考同時希望大神們可以指正出其中理解存在偏差的地方。
本部落格為本人原創, 轉載請告知.
相關推薦
volatile為什麼不能保證原子性的思考
1、故事的起因: 前幾天有人問過關於volatile關鍵字的一些問題, 想起了自己剛接觸這個關鍵字時候存在的疑惑,趕上今天有時間就整理了一些自己的想法。 首先,需要明確的一點是,volatile關鍵字修飾的變數可以保證該變數操作的有序性和可
為什麼volatile不能保證原子性而Atomic可以?
在上篇《非阻塞同步演算法與CAS(Compare and Swap)無鎖演算法》中講到在Java中long賦值不是原子操作,因為先寫32位,再寫後32位,分兩步操作,而AtomicLong賦值是原子操作,為什麼?為什麼volatile能替代簡單的鎖,卻不能保證原子性?這裡面涉及volatile,是java中的
volatile不能保證原子性,也就不能保證執行緒安全
volatile只能保證變數的可見性,無法保證對變數的操作的原子性。 還是以最常用的i++來說吧,包含3個步驟 1,從記憶體讀取i當前的值 2,加1 3,把修改後的值重新整理到記憶體 對於普通變數來說多執行緒下1,2之間被中斷,其
volatile 可以保證可見性,但不能保證原子性
轉自:http://blog.csdn.net/shukebai/article/details/51163068 在Java執行緒併發處理中,有一個關鍵字volatile的使用目前存在很大的混淆,以為使用這個關鍵字,在進行多執行緒併發處理的時候就可以萬事大吉。 J
為什麼volatile不能保證原子性而Atomic可以?(r)
在上篇《非阻塞同步演算法與CAS(Compare and Swap)無鎖演算法》中講到在Java中long賦值不是原子操作,因為先寫32位,再寫後32位,分兩步操作,而AtomicLong賦值是原子操作,為什麼?為什麼volatile能替代簡單的鎖,卻不能保證原子性?
Java併發程式設計之驗證volatile不能保證原子性
Java併發程式設計之驗證volatile不能保證原子性 通過系列文章的學習,凱哥已經介紹了volatile的三大特性。1:保證可見性 2:不保證原子性 3:保證順序。那麼怎麼來驗證可見性呢?本文凱哥(凱哥Java:kaigejava)將通過程式碼演示來證明為什麼說volatile不能夠保證共享變數的原子性操
Volatile不保證原子性(二)
Volatile不保證原子性 前言 通過前面對JMM的介紹,我們知道,各個執行緒對主記憶體中共享變數的操作都是各個執行緒各自拷貝到自己的工作記憶體進行操作後在寫回到主記憶體中的。 這就可能存在一個執行緒AAA修改了共享變數X的值,但是還未寫入主記憶體時,另外一個執行緒BBB又對主記憶體中同一共享變數X進行操作
深入理解Atomic原子操作和volatile非原子性
log tile 修飾 深入 clas 同時 結果 一個 body 原子操作可以理解為: 一個數,很多線程去同時修改它,不加sync同步鎖,就可以保證修改結果是正確的 Atomic正是采用了CAS算法,所以可以在多線程環境下安全地操作對象。 volatile是Java的關鍵
Java關鍵字volatile,原子性,變數可見性
The volatile keyword also ensures visibility across the application. If you declare a field to be volatile, this means that as soon as a write occurs for t
編寫安全程式碼:小心volatile的原子性誤解
這時,exit_flag都需要使用volatile來修飾。不然對於執行緒1的程式碼,如果編譯器發現線上程1的程式碼中沒有任何地方修改exit_flag,有可能會將exit_flag放入暫存器快取。這樣,在每次的條件檢查的時候,都是從暫存器中讀取,而非exit_flag對應的記憶體。這樣將導致每次讀取的值都為0
volatile是非原子性 AtomicInteger原子性
import java.util.concurrent.atomic.AtomicInteger; /* * volatile不是原子性的,它只保證變數在多執行緒中可見,操作時並不是執行緒安全的 * AtomicInteger 是原子性的 */ public clas
volatile不能保證程式執行的原子性以及只能一定程度上保證有序性
多執行緒併發程式設計如何正確的執行程式: 1、原子性:執行過程要麼成功要麼失敗,比如經典的銀行轉賬問題。 2、可見性:多執行緒併發時,一個執行緒修改了工作記憶體中的值(主存中的值),會立刻改變主存相應地址的值,其它執行緒工作記憶體的值無效,重新獲取主存的值。 3、有序性:程
Java線程安全 關於原子性與volatile的試驗
har length rgs 無法 over pub boolean 即使 dex 1. 變量遞增試驗 1 static /*volatile*/ int shared=0;//volatile也無法保證++操作的原子性 2 static synchr
Java-JUC(二):volatile對Java內存模型中的可見性、原子性、有序性影響
UC volatil 可見 模型 原子性 有序性 juc 內存模型 volatile Java內存模型 Java內存模型-可見性 Java內存模型-原子性 Java內存模型-有序性 volatile-是否具有可見性? volatile
java併發程式設計之利用CAS保證操作的原子性
import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; public class Counter { private AtomicInteger at
從volatile分析i++和++i非原子性問題
目錄 1、可見性(Visibility) 2、原子性(Atomicity) 3、Java記憶體模型的抽象結構( JMM ) 4、volatile 5、 多執行緒下的i++問題 5、自定義實現i++原子操作 5.1
Java併發程式設計:volatile關鍵字解析-原子性,可見性,有序性
volatile這個關鍵字可能很多朋友都聽說過,或許也都用過。在Java 5之前,它是一個備受爭議的關鍵字,因為在程式中使用它往往會導致出人意料的結果。在Java 5之後,volatile關鍵字才得以重獲生機。 volatile關鍵字雖然從字面上理解起來比較簡單,但是要用好不是一件容易的事情
synchronized和volatile——原子性和可見性
<div class="htmledit_views"> <h1 style="color:rgb(51,51,51);font-family:Arial;line-height:26px;text-align:center
atomic保證記憶體變數的原子性
import java.util.concurrent.atomic.AtomicInteger; /** * *atomic保證原子性的操作 */ public class TestAtomic { /** * 原子性 * AtomicInteger 可以保證原子操
redis實現秒殺功能例子(採用lua的原子性保證資料的一致性)
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.spring