volatile能否保證線程安全 java里volatile關(guān)鍵字有什么特性是否能保證線程安全?
java里volatile關(guān)鍵字有什么特性是否能保證線程安全?1. Volatile不能保證原子性。簡而言之,Java有所謂的主存區(qū)和線程棧。在主內(nèi)存區(qū)域和每個線程的堆棧中都有相同變量的副本(一對多)
java里volatile關(guān)鍵字有什么特性是否能保證線程安全?
1. Volatile不能保證原子性。簡而言之,Java有所謂的主存區(qū)和線程棧。在主內(nèi)存區(qū)域和每個線程的堆棧中都有相同變量的副本(一對多)。volatile提供的可見性意味著當每個線程訪問volatile修改的變量時,volatile確保線程可以從主存加載最新的值(相反,修改線程后同步到主存的值也應該對其他線程可見);
2。Java的volatile的語義實際上并不涉及CPU緩存。JVM本身是一個軟件抽象,它已經(jīng)在操作系統(tǒng)之上了。由于非原子性,volatile不能保證線程安全。如果只有簡單的讀寫操作,比如set I=2,get I,就可以認為是安全的。4Volatile被認為比lock更輕,編程更簡單??梢允褂胿olatile的地方:對于一個變量,更新它的值不依賴于當前值,并且該變量不會與其他變量形成一個不可變的條件。
多個線程可以讀一個變量,只有一個線程可以對這個變量進行寫,到底要不要加鎖?
下面簡要說明以下原因:
鎖定是因為操作不是原子的。讓我們用操作一來解釋它。看下面兩個圖。
我這個操作需要
看上面的第二個圖,你能很清楚地理解這個過程嗎?
鎖定是為了確保上述三個步驟是原子操作。
回到問題上來,只有一個線程要寫,沒有競爭,所以不需要鎖定。
但是,如果你看第一張圖片,因為主內(nèi)存和本地內(nèi)存的存在
在一個線程寫入后,其他線程無法立即看到它。這就是可見性問題。
添加volatile關(guān)鍵字后,它將在操作后強制工作內(nèi)存和主內(nèi)存同步,以確保其他線程可以立即看到它。
volatile關(guān)鍵字在Java中有什么作用?
Volatile是為了防止指令重新排序以確??梢娦?/p>
對于JVM級別,是為了防止編譯器重新排序
同時,對于某些CPU,它們會通過緩存鎖或線程來解決緩存可見性
但是,目前很多CPU已經(jīng)過優(yōu)化,由于cache一致性MESI會帶來性能開銷,因此采用storebuffer機制進行異步處理,這種機制會導致指令的無序執(zhí)行。這會導致可見性問題。
那么volatile會在CPU級別增加內(nèi)存屏障,解決CPU執(zhí)行無序?qū)е碌目梢娦詥栴}
因為volatile不能保證它的原子性,它只保證修改后一個線程對其他線程可見,特別是當多線程自動增減一個變量時,會導致變量錯誤。參考《深入理解Java虛擬機》一書,volatile用于以下場景:
1>操作結(jié)果不依賴于變量的當前值,或者可以確保只有一個線程修改變量的值。
2>變量不需要與其他狀態(tài)變量一起參與不變約束。因此,在使用volatile關(guān)鍵字時,應該小心。不僅僅是簡單的類型變量被volatile修改。此變量上的所有操作都是原始操作。當一個變量的值由它以前的值決定時,例如n=n1,n volatile關(guān)鍵字將無效。只有當一個變量的值與其前一個值無關(guān)時,對該變量的操作才能是原子級的,例如n=m1,這是原始級別。因此,在使用volatile鍵時必須小心。如果不確定,可以使用synchronized而不是volatile。