在实践中阅读并发时,我读到了
没有可见性证明了不充分的方式 同步程序可能会导致令人惊讶的结果:过时的数据。当时 读者线程检查准备好了,它可能会看到过时的价值。除非 每次访问变量时都会使用同步,它是 可以看到该变量的陈旧值。更糟糕的是,陈旧性是 不是全部或全部:线程可以看到一个变量的最新值 但是第一个写的另一个变量的陈旧值
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;
}
}
我没有得到陈旧的意思。两个线程共享相同的引用,如何将一个修改后的值永远不会被其他线程看到?
答案 0 :(得分:6)
这不是Java特定的问题。处理器有缓存。如果没有同步,处理器1可以从内存中读取一个值进入其缓存,在其缓存中修改它但从不刷新它,然后处理器2从内存中读取陈旧值
答案 1 :(得分:5)
每个线程都有自己的堆栈,因此它可以拥有自己的变量副本 访问。创建线程时,它会复制all的值 可访问的变量在自己的内存中。使用volatile关键字 要对jvm“ 警告说,这个变量可以在另一个中修改 线程 “。如果没有这个关键字,JVM可以自由地制作一些 优化,例如 永不刷新 某些本地副本 线程。 volatile强制线程更新原始变量 对于每个变量。
来源DZone
硬件实施
这是因为为快速访问变量而进行的处理器优化。当变量保存在缓存中时,访问速度比每次访问内存要快得多。因此,为了刷新缓存,您需要说volatile
并刷新缓存并重建它,因为此变量正在其他线程中修改。
static
变量的缓存也已完成,由于相同的原因快速访问,它们也有资格进行缓存。所以,您对volatile
变量也需要static
。
另见:
答案 2 :(得分:3)
它的主要意图是同时设置number = 42和ready = true,但由于它们是按照这个特定的顺序调用的,因此你的ReaderThread存在竞争条件(可能会打印出数字,即使我们真的不想要它。
他们希望您同步这两个设置在一起的变量。
答案 3 :(得分:3)
此问题与memory visibility
多线程问题有关。
如果要读取/获取可由多个thred修改的对象的值;那你需要小心。让我们举一个示例来强调这一点:
public class XYZ{
private String state;
public synchronized setState(..){
//set state
}
public String getState(){
return state;
}
}
上面的示例不同步返回状态的getter方法。因此,您可能会收到过时的数据(即使是静态的)
获取最新数据的唯一方法是同步get方法或将状态声明为volatile。