We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
读了一次“错误”的并发控制引发的思考一文,觉得有些疑问。对于下面的代码:
class MultiProcessorTask { private boolean flag = true; public void runMethod() { while (flag) { synchronized (new Simple(1)){} } } public void stopMethod() { System.out.println("change 'flag' field ..."); flag = false; } }
原文的观点似乎倾向于synchronized带来的happens-before规则可以保证对flag的可见性,所以需要用JVM参数-XX:-EliminateLocks关闭锁消除优化就行了。 我的疑问在于:
synchronized
flag
-XX:-EliminateLocks
所以我把代码改写成了没有锁和volatile:
volatile
package test; class MultiProcessorTask { private boolean flag = true; long sum = 0L; public void runMethod() { while (flag) { long a = System.currentTimeMillis() % 9; if (a == 1L) { sum += a; } } System.out.println("Result: " + sum); } public void stopMethod() throws InterruptedException { System.out.println("准备睡眠1秒,然后置flag为false."); Thread.sleep(1000); System.out.println("change 'flag' field ..."); flag = false; } } class ThreadA extends Thread { private MultiProcessorTask task; ThreadA(MultiProcessorTask task) {this.task = task;} @Override public void run() { task.runMethod(); } } public class TestRun { public static void main(String[] args) throws InterruptedException { MultiProcessorTask task = new MultiProcessorTask(); ThreadA a = new ThreadA(task); a.start(); task.stopMethod(); System.out.println("it's over"); } }
直接运行,不会退出,加上JVM参数-Xint解释执行,会退出,这一步就说明了这个锅还是JIT的,下面通过jitwatch看一下JIT优化后的汇编代码。使用的JVM参数是:
-Xint
-server -XX:+UnlockDiagnosticVMOptions -XX:+TraceClassLoading -XX:+PrintAssembly -XX:+LogCompilation -XX:LogFile=live.log
没有volatile时:
L0001: movabs $0x108289ce4,%r10 0x00000001117cc1f6: callq *%r10 ;*invokestatic currentTimeMillis ; - test.MultiProcessorTask::runMethod@7 (line 10) 0x00000001117cc1f9: mov %rax,%r10 0x00000001117cc1fc: mov %rax,%r11 0x00000001117cc1ff: sar $0x3f,%r11 0x00000001117cc203: movabs $0x1c71c71c71c71c72,%rax 0x00000001117cc20d: mov %r10,%r8 0x00000001117cc210: imul %r10 0x00000001117cc213: sub %r11,%rdx ;*lrem ; - test.MultiProcessorTask::runMethod@13 (line 10) 0x00000001117cc216: mov %rdx,%r10 0x00000001117cc219: shl $0x3,%r10 0x00000001117cc21d: add %rdx,%r10 0x00000001117cc220: mov %r8,%r11 0x00000001117cc223: sub %r10,%r11 0x00000001117cc226: cmp $0x1,%r11 0x00000001117cc22a: jne L0002 ;*ifne ;如果余数不是1 ; - test.MultiProcessorTask::runMethod@18 (line 11) 0x00000001117cc22c: incq 0x10(%rbp) ; OopMap{rbp=Oop off=144} ;*goto ; - test.MultiProcessorTask::runMethod@31 (line 14) L0002: test %eax,-0xa279236(%rip) # 0x0000000107553000 ;*goto ; - test.MultiProcessorTask::runMethod@31 (line 14) ; {poll} *** SAFEPOINT POLL *** 0x00000001117cc236: jmp L0001 L0003: xor %ebp,%ebp 0x00000001117cc23a: jmp L0000
可以看出,里面形成了一个死循环,不再判断flag的值,甚至也不把为1的余数加到sum中,每次循环只是取当前时间,然后取余。 而给flag加上volatile后的汇编代码为:
sum
0x0000000119be7861: jmp L0002 L0000: mov 0x10(%rbx),%r10 ;*getfield sum; 余数是1时跳到这里,取sum加总 ; - test.MultiProcessorTask::runMethod@23 (line 12) 0x0000000119be7867: add $0x1,%r10 0x0000000119be786b: mov %r10,0x10(%rbx) ;*putfield sum ; - test.MultiProcessorTask::runMethod@28 (line 12) 0x0000000119be786f: nop ; OopMap{rbx=Oop off=80} ;*goto ; - test.MultiProcessorTask::runMethod@31 (line 14) L0001: test %eax,-0xc6fd876(%rip) # 0x000000010d4ea000; 余数不是1跳到这里,取flag测试继续循环 ;*aload_0 ; - test.MultiProcessorTask::runMethod@0 (line 9) ; {poll} *** SAFEPOINT POLL *** L0002: movzbl 0xc(%rbx),%r11d ;*getfield flag ; - test.MultiProcessorTask::runMethod@1 (line 9) 0x0000000119be787b: test %r11d,%r11d; 测试flag是不是为false 0x0000000119be787e: je L0003 ;*ifeq; 是false,跳到L0003退出循环 ; - test.MultiProcessorTask::runMethod@4 (line 9) 0x0000000119be7880: movabs $0x10e289ce4,%r10 0x0000000119be788a: callq *%r10 ;*invokestatic currentTimeMillis ; - test.MultiProcessorTask::runMethod@7 (line 10) 0x0000000119be788d: mov %rax,%r11 0x0000000119be7890: movabs $0x1c71c71c71c71c72,%rax 0x0000000119be789a: imul %r11 0x0000000119be789d: mov %r11,%r10 0x0000000119be78a0: sar $0x3f,%r10 0x0000000119be78a4: sub %r10,%rdx ;*lrem ; - test.MultiProcessorTask::runMethod@13 (line 10) 0x0000000119be78a7: mov %rdx,%r10 0x0000000119be78aa: shl $0x3,%r10 0x0000000119be78ae: add %rdx,%r10 0x0000000119be78b1: sub %r10,%r11 0x0000000119be78b4: cmp $0x1,%r11 0x0000000119be78b8: je L0000 ;*ifne; 如果余数是1,那么跳到L0000 ; - test.MultiProcessorTask::runMethod@18 (line 11) 0x0000000119be78ba: jmp L0001; 余数不是1,跳到L0001 L0003: mov $0xffffff65,%esi
代码不同一目了然了。所以,在针对单个变量的前提下,不管是volatile还是加锁各种花式操作,所针对的都不是硬件层面上的可见性问题,而是如何阻止JIT激进优化的问题。 两次汇编代码的优化级别都是: 其实,我的例子在不加volatile的情况下使用JVM参数-XX:-UseOnStackReplacement也能正常退出。
-XX:-UseOnStackReplacement
The text was updated successfully, but these errors were encountered:
No branches or pull requests
读了一次“错误”的并发控制引发的思考一文,觉得有些疑问。对于下面的代码:
原文的观点似乎倾向于
synchronized
带来的happens-before规则可以保证对flag
的可见性,所以需要用JVM参数-XX:-EliminateLocks
关闭锁消除优化就行了。我的疑问在于:
所以我把代码改写成了没有锁和
volatile
:直接运行,不会退出,加上JVM参数
-Xint
解释执行,会退出,这一步就说明了这个锅还是JIT的,下面通过jitwatch看一下JIT优化后的汇编代码。使用的JVM参数是:没有
volatile
时:可以看出,里面形成了一个死循环,不再判断
flag
的值,甚至也不把为1的余数加到sum
中,每次循环只是取当前时间,然后取余。而给
flag
加上volatile
后的汇编代码为:代码不同一目了然了。所以,在针对单个变量的前提下,不管是
volatile
还是加锁各种花式操作,所针对的都不是硬件层面上的可见性问题,而是如何阻止JIT激进优化的问题。两次汇编代码的优化级别都是:
其实,我的例子在不加
volatile
的情况下使用JVM参数-XX:-UseOnStackReplacement
也能正常退出。The text was updated successfully, but these errors were encountered: