2 回答
TA贡献1847条经验 获得超7个赞
你从一个完全错误的角度看这个。首先,您引用的是JLS
而不是谈论flush,这将是该规范的实现细节。您唯一需要依赖的绝对是 JLS,其他任何事情都可以知道可能是,但并不能证明任何形状或形式的规范是对还是错。
你错的根本地方是:
我肯定知道常规锁获取不是真的......
在实践中x86
,您可能是对的,但JLS
官方 oracle 教程要求:
当一个线程释放一个内在锁时,在该操作和相同锁的任何后续获取之间建立了一个happens-before 关系。
Happens-before 是为后续动作建立的(如果你愿意,请阅读两个对你来说更简单的动作)。一个线程释放锁,另一个获取它 - 这些是后续 ( release-acquire semantics
)。
同样的事情发生在一个volatile
- 一些线程写入它,并且当另一个线程通过后续读取观察到写入时,建立了happens-before。
TA贡献1784条经验 获得超9个赞
这是否意味着,对 volatile 变量的任何写入都会使执行线程将其缓存刷新到主内存中,并且每次从 volatile 字段读取都会使线程从主内存中重新读取其变量?
不,不是那个意思。那样想是一个常见的错误。它的全部含义是 Java 内存模型中指定的内容。
在英特尔 CPU 上,有刷新缓存行的指令:clflush并且clflushopt在发生易失性写入时对整个缓存行进行这种刷新是非常低效的。
为了提供一个例子,让我们看看 volatile 变量是如何实现的(对于这个例子)
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)
为了我的哈斯韦尔。让我们写一个简单的例子:
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 编译器运行它,如下所示:
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
因此,在这个简单的示例中,volatile编译为locked 指令,要求缓存行具有exclusive要执行的状态(如果不是,则可能向其他内核发送读取无效信号)。
添加回答
举报