Java 中 long 是不是原子操作?
點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)
作者:LouisWong
來源:https://my.oschina.net/u/1753415/blog/724242
Java中l(wèi)ong和double的原子性
java中基本類型中,long和double的長度都是8個(gè)字節(jié),32位(4字節(jié))處理器對(duì)其讀寫操作無法一次完成,那么,JVM,long和double是原子性的嗎?
JVM中對(duì)long的操作是不是原子操作?
首先,通過一段程序?qū)ong的原子性進(jìn)行判斷。測試程序如下:
public class LongAtomTest implements Runnable {
private static long field = 0;
private volatile long value;
public long getValue() {
return value;
}
public void setValue(long value) {
this.value = value;
}
public LongAtomTest(long value) {
this.setValue(value);
}
@Override
public void run() {
int i = 0;
while (i < 100000) {
LongAtomTest.field = this.getValue();
i++;
long temp = LongAtomTest.field;
if (temp != 1L && temp != -1L) {
System.out.println("出現(xiàn)錯(cuò)誤結(jié)果" + temp);
System.exit(0);
}
}
System.out.println("運(yùn)行正確");
}
public static void main(String[] args) throws InterruptedException {
// 獲取并打印當(dāng)前JVM是32位還是64位的
String arch = System.getProperty("sun.arch.data.model");
System.out.println(arch+"-bit");
LongAtomTest t1 = new LongAtomTest(1);
LongAtomTest t2 = new LongAtomTest(-1);
Thread T1 = new Thread(t1);
Thread T2 = new Thread(t2);
T1.start();
T2.start();
T1.join();
T2.join();
}
}
如果對(duì)long的寫入和讀取操作是原子性的,那么,field的值只可能是1或者-1
運(yùn)行結(jié)果如下:
32-bit
出現(xiàn)錯(cuò)誤結(jié)果-4294967295
運(yùn)行正確
可以看出,當(dāng)線程t1,t2同時(shí)對(duì)long進(jìn)行寫的時(shí)候,long出現(xiàn)了既不是t1寫入的值,又不是t2寫入的值。46 張 PPT 弄懂 JVM 調(diào)優(yōu),推薦看下。
可以推測,jvm中對(duì)long的操作并非原子操作。
為什么對(duì)long的操作不是原子的?
JVM內(nèi)存模型中定義了8中原子操作:
lock:將一個(gè)變量標(biāo)識(shí)為被一個(gè)線程獨(dú)占狀態(tài) unclock:將一個(gè)變量從獨(dú)占狀態(tài)釋放出來,釋放后的變量才可以被其他線程鎖定 read:將一個(gè)變量的值從主內(nèi)存?zhèn)鬏數(shù)焦ぷ鲀?nèi)存中,以便隨后的load操作 load:把read操作從主內(nèi)存中得到的變量值放入工作內(nèi)存的變量的副本中 use:把工作內(nèi)存中的一個(gè)變量的值傳給執(zhí)行引擎,每當(dāng)虛擬機(jī)遇到一個(gè)使用到變量的指令時(shí)都會(huì)使用該指令 assign:把一個(gè)從執(zhí)行引擎接收到的值賦給工作內(nèi)存中的變量,每當(dāng)虛擬機(jī)遇到一個(gè)給變量賦值的指令時(shí),都要使用該操作 store:把工作內(nèi)存中的一個(gè)變量的值傳遞給主內(nèi)存,以便隨后的write操作 write:把store操作從工作內(nèi)存中得到的變量的值寫到主內(nèi)存中的變量
其中,與賦值,取值相關(guān)的包括 read,load,use,assign,store,write
按照這個(gè)規(guī)定,long的讀寫都是原子操作,與我們的實(shí)踐結(jié)果相反,為什會(huì)導(dǎo)致這種問題呢?
對(duì)于32位操作系統(tǒng)來說,單次次操作能處理的最長長度為32bit,而long類型8字節(jié)64bit,所以對(duì)long的讀寫都要兩條指令才能完成(即每次讀寫64bit中的32bit)。
如果JVM要保證long和double讀寫的原子性,勢必要做額外的處理。那么,JVM有對(duì)這一情況進(jìn)行額外處理嗎?另外,JVM 系列面試題和答案全部整理好了,微信搜索Java技術(shù)棧,在后臺(tái)發(fā)送:面試,可以在線閱讀。
針對(duì)這一問題可以參考Java語言規(guī)范文檔:jls-17 Non-Atomic Treatment of double and long
Writes and reads of volatile long and double values are always atomic.
從規(guī)定中我們可以知道:
對(duì)于64位的long和double,如果沒有被volatile修飾,那么對(duì)其操作可以不是原子的。在操作的時(shí)候,可以分成兩步,每次對(duì)32位操作。 如果使用volatile修飾long和double,那么其讀寫都是原子操作 對(duì)于64位的引用地址的讀寫,都是原子操作 在實(shí)現(xiàn)JVM時(shí),可以自由選擇是否把讀寫long和double作為原子操作 推薦JVM實(shí)現(xiàn)為原子操作
從程序得到的結(jié)果來看,32位的HotSpot沒有把long和double的讀寫實(shí)現(xiàn)為原子操作。在讀寫的時(shí)候,分成兩次操作,每次讀寫32位。因?yàn)椴捎昧诉@種策略,所以64位的long和double的讀與寫都不是原子操作。
在硬件,操作系統(tǒng),JVM都是64位的情況下呢?
對(duì)于64bit的環(huán)境來說,單次操作可以操作64bit的數(shù)據(jù),即可以以一次性讀寫long或double的整個(gè)64bit。因此我們可以猜測,在64位的環(huán)境下,long和double的讀寫有可能是原子操作。在換了64位的JVM之后,多次運(yùn)行,結(jié)果都是正確的
64-bit
運(yùn)行正確
運(yùn)行正確
結(jié)果表明,在64bit的虛擬機(jī)下,long的處理是原子性的。最后,關(guān)注公眾號(hào)Java技術(shù)棧,在后臺(tái)回復(fù):面試,可以獲取我整理的 JVM 系列面試題和答案,非常齊全。






關(guān)注Java技術(shù)??锤喔韶?/strong>


