Open
Description
锁还是打印
在 #11 的基础上,把代码再改写为:
package test;
class MultiProcessorTask {
private boolean flag = true;
public void runMethod() {
while (flag) {
System.out.println(1);
}
}
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");
}
}
这样也能正常退出,众所周知,打印里面是有锁的,所以这里是打印还是锁阻止了JIT激进优化?不知道,对JIT优化的过程了解很少,我赌五毛是打印。
定时更新,定时读取
package test;
public class TestRun {
private static int version = 0;
private static class Writer implements Runnable {
@Override
public void run() {
while (true) {
try {
Thread.sleep(50);
version += 1;
System.out.println("Writer更新为: " + version + ", 时间: " + System.currentTimeMillis());
} catch (InterruptedException ignore) {
}
}
}
}
private static class Reader implements Runnable {
private final int index;
private final long sleepTime;
private Reader(int index, long sleepTime) {
this.index = index;
this.sleepTime = sleepTime * 10;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(sleepTime);
System.out.println("Read" + index + "读到: " + version + ", 时间: " + System.currentTimeMillis());
} catch (InterruptedException ignore) {
}
}
}
}
public static void main(String[] args) {
new Thread(new Reader(1, 3)).start();
new Thread(new Reader(2, 4)).start();
new Thread(new Writer()).start();
}
}
一个写线程每隔50毫秒把version
加一,两个读线程分别每隔30毫秒和40毫秒读一次version
值,测试执行了147089次,触发了最高级别的C2/OSR/Level 4编译,在这种情况下仍可以保证Reader线程读到最新值,如下:
Writer更新为: 147087, 时间: 1593965931595
Read1读到: 147087, 时间: 1593965931603
Read2读到: 147087, 时间: 1593965931612
Read1读到: 147087, 时间: 1593965931635
Writer更新为: 147088, 时间: 1593965931646
Read2读到: 147088, 时间: 1593965931656
Read1读到: 147088, 时间: 1593965931665
Read1读到: 147088, 时间: 1593965931698
Writer更新为: 147089, 时间: 1593965931698
Read2读到: 147089, 时间: 1593965931698
Read1读到: 147089, 时间: 1593965931733
Read2读到: 147089, 时间: 1593965931739
编译级别:
至此彻底说明了,对于单个变量的并发读写在硬件层面上根本无需同步,给我们造成"不可见"现象的真正原因是JIT的激进优化。当然,JMM规范还是应当遵守的,毕竟不能保证所在的场景就一定不会被JIT做些手脚。这几个例子的用意是说清楚长久以来在🧠里的一些混乱。
Metadata
Metadata
Assignees
Labels
No labels