DD.*_*DD. 9 java concurrency synchronization java-memory-model
"Java Concurrency in Practice"给出了以下不安全类的示例,由于Java内存模型的性质可能最终会永久运行或打印0.
这个类试图证明的问题是这里的变量不是线程之间的"共享".因此,线程看到的值可能与另一个线程不同,因为它们不是易失性或同步的.另外由于JVM ready = true允许的语句的重新排序可能在number = 42之前设置.
对我来说,这个类总是使用JVM 1.6正常工作.有关如何让这个类执行不正确的行为(即打印0或永远运行)的任何想法?
public class NoVisibility {
private static boolean ready;
private static int number;
private static class ReaderThread extends Thread {
public void run() {
while (!ready)
Thread.yield();
System.out.println(number);
}
}
public static void main(String[] args) {
new ReaderThread().start();
number = 42;
ready = true;
}
}
Run Code Online (Sandbox Code Playgroud)
java内存模型定义了工作所需的内容和不需要的内容.不安全的多线程代码的"美"在大多数情况下(特别是在受控的开发环境中)通常都有效.只有当你使用更好的计算机进行生产并且负载增加时,JIT才能真正开始尝试使用错误.
您遇到的问题是您没有等待足够长的时间来优化代码并缓存值.
当x86_64系统上的线程第一次读取值时,它将获得一个线程安全副本.它只是后来的变化,它无法看到.其他CPU可能不是这种情况.
如果您尝试这样做,您可以看到每个线程都卡在其本地值.
public class RequiresVolatileMain {
static volatile boolean value;
public static void main(String... args) {
new Thread(new MyRunnable(true), "Sets true").start();
new Thread(new MyRunnable(false), "Sets false").start();
}
private static class MyRunnable implements Runnable {
private final boolean target;
private MyRunnable(boolean target) {
this.target = target;
}
@Override
public void run() {
int count = 0;
boolean logged = false;
while (true) {
if (value != target) {
value = target;
count = 0;
if (!logged)
System.out.println(Thread.currentThread().getName() + ": reset value=" + value);
} else if (++count % 1000000000 == 0) {
System.out.println(Thread.currentThread().getName() + ": value=" + value + " target=" + target);
logged = true;
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
打印以下内容显示其掠过值,但卡住了.
Sets true: reset value=true
Sets false: reset value=false
...
Sets true: reset value=true
Sets false: reset value=false
Sets true: value=false target=true
Sets false: value=true target=false
....
Sets true: value=false target=true
Sets false: value=true target=false
Run Code Online (Sandbox Code Playgroud)
如果我添加-XX:+PrintCompilation此切换发生在您看到的时间
1705 1 % RequiresVolatileMain$MyRunnable::run @ -2 (129 bytes) made not entrant
1705 2 % RequiresVolatileMain$MyRunnable::run @ 4 (129 bytes)
Run Code Online (Sandbox Code Playgroud)
这表明代码已编译为本机是一种非线程安全的方式.
如果你创造价值volatile你会看到它无休止地翻转价值(或直到我感到无聊)
编辑:这个测试的作用是什么; 当它检测到的值不是线程目标值时,它会设置该值.即.线程0设置为true和线程1设置为false 当两个线程正确共享字段时,它们会看到彼此的更改,并且值始终在true和false之间翻转.
如果没有volatile,则会失败并且每个线程只看到自己的值,因此它们都会更改值,线程0看到true并且线程1看到false相同的字段.
| 归档时间: |
|
| 查看次数: |
849 次 |
| 最近记录: |