2 回答

TA貢獻(xiàn)1847條經(jīng)驗(yàn) 獲得超7個(gè)贊
你從一個(gè)完全錯(cuò)誤的角度看這個(gè)。首先,您引用的是JLS
而不是談?wù)?em>flush,這將是該規(guī)范的實(shí)現(xiàn)細(xì)節(jié)。您唯一需要依賴(lài)的絕對(duì)是 JLS,其他任何事情都可以知道可能是,但并不能證明任何形狀或形式的規(guī)范是對(duì)還是錯(cuò)。
你錯(cuò)的根本地方是:
我肯定知道常規(guī)鎖獲取不是真的......
在實(shí)踐中x86
,您可能是對(duì)的,但JLS
官方 oracle 教程要求:
當(dāng)一個(gè)線程釋放一個(gè)內(nèi)在鎖時(shí),在該操作和相同鎖的任何后續(xù)獲取之間建立了一個(gè)happens-before 關(guān)系。
Happens-before 是為后續(xù)動(dòng)作建立的(如果你愿意,請(qǐng)閱讀兩個(gè)對(duì)你來(lái)說(shuō)更簡(jiǎn)單的動(dòng)作)。一個(gè)線程釋放鎖,另一個(gè)獲取它 - 這些是后續(xù) ( release-acquire semantics
)。
同樣的事情發(fā)生在一個(gè)volatile
- 一些線程寫(xiě)入它,并且當(dāng)另一個(gè)線程通過(guò)后續(xù)讀取觀察到寫(xiě)入時(shí),建立了happens-before。

TA貢獻(xiàn)1784條經(jīng)驗(yàn) 獲得超9個(gè)贊
這是否意味著,對(duì) volatile 變量的任何寫(xiě)入都會(huì)使執(zhí)行線程將其緩存刷新到主內(nèi)存中,并且每次從 volatile 字段讀取都會(huì)使線程從主內(nèi)存中重新讀取其變量?
不,不是那個(gè)意思。那樣想是一個(gè)常見(jiàn)的錯(cuò)誤。它的全部含義是 Java 內(nèi)存模型中指定的內(nèi)容。
在英特爾 CPU 上,有刷新緩存行的指令:clflush并且clflushopt在發(fā)生易失性寫(xiě)入時(shí)對(duì)整個(gè)緩存行進(jìn)行這種刷新是非常低效的。
為了提供一個(gè)例子,讓我們看看 volatile 變量是如何實(shí)現(xiàn)的(對(duì)于這個(gè)例子)
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)
為了我的哈斯韋爾。讓我們寫(xiě)一個(gè)簡(jiǎn)單的例子:
public static volatile long a = 0;
public static void main(String[] args){
Thread t1 = new Thread(() -> {
while(true){
//to avoid DCE
if(String.valueOf(String.valueOf(a).hashCode()).equals(String.valueOf(System.nanoTime()))){
System.out.print(a);
}
}
});
Thread t2 = new Thread(() -> {
while(true){
inc();
}
});
t1.start();
t2.start();
}
public static void inc(){
a++;
}
我禁用了分層編譯并使用 C2 編譯器運(yùn)行它,如下所示:
java -server -XX:-TieredCompilation -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*Volatile.inc -jar target/test-0.0.1.jar
輸出如下:
# {method} {0x00007f87d87c6620} 'inc' '()V' in 'com/test/Volatlee'
# [sp+0x20] (sp of caller)
0x00007f87d1085860: sub $0x18,%rsp
0x00007f87d1085867: mov %rbp,0x10(%rsp) ;*synchronization entry
; - com.test.Volatlee::inc@-1 (line 26)
0x00007f87d108586c: movabs $0x7191fab68,%r10 ; {oop(a 'java/lang/Class' = 'com/test/Volatlee')}
0x00007f87d1085876: mov 0x68(%r10),%r11
0x00007f87d108587a: add $0x1,%r11
0x00007f87d108587e: mov %r11,0x68(%r10)
0x00007f87d1085882: lock addl $0x0,(%rsp) ;*putstatic a
; - com.test.Volatlee::inc@5 (line 26)
0x00007f87d1085887: add $0x10,%rsp
0x00007f87d108588b: pop %rbp
0x00007f87d108588c: test %eax,0xca8376e(%rip) ; {poll_return}
0x00007f87d1085892: retq
;tons of hlt ommited
因此,在這個(gè)簡(jiǎn)單的示例中,volatile編譯為locked 指令,要求緩存行具有exclusive要執(zhí)行的狀態(tài)(如果不是,則可能向其他內(nèi)核發(fā)送讀取無(wú)效信號(hào))。
添加回答
舉報(bào)