Java 同步前更新内存?
Java内存模型中提到:当线程退出同步块作为释放相关监视器的一部分时,JMM要求将本地处理器缓存刷新到主存。类似地,作为进入同步块时获取监视器的一部分,本地缓存将失效,以便后续读取将直接进入主内存,而不是本地缓存。 那么,为什么在代码中我必须声明实例为volatile,因为当第二个线程进入同步块时,它将直接进入主内存Java 同步前更新内存?,java,multithreading,synchronization,volatile,double-checked-locking,Java,Multithreading,Synchronization,Volatile,Double Checked Locking,Java内存模型中提到:当线程退出同步块作为释放相关监视器的一部分时,JMM要求将本地处理器缓存刷新到主存。类似地,作为进入同步块时获取监视器的一部分,本地缓存将失效,以便后续读取将直接进入主内存,而不是本地缓存。 那么,为什么在代码中我必须声明实例为volatile,因为当第二个线程进入同步块时,它将直接进入主内存 public final class MySingleton { private static MySingleton instance = null; private M
public final class MySingleton {
private static MySingleton instance = null;
private MySingleton() { }
public static MySingleton getInstance() {
if (instance == null) {
synchronized (MySingleton.class) {
if (instance == null) {
instance = new MySingleton();
}
}
}
return instance;
}
}
我的意思是,当另一个线程进入同步块并进行第二次检查时,它应该像前面提到的那样从主内存更新。您必须声明它为volatile,否则无法保证对getInstance()的两次调用将返回同一个实例 不能保证主内存会被访问,只有缓存一致的值。i、 e.所有螺纹将看到相同的值 顺便说一句:你当然知道它比需要的复杂得多。你所需要的只是
public enum MySingleton {
INSTANCE;
}
做同样的事情。您必须声明它为volatile,否则无法保证对getInstance()的两次调用将返回相同的实例 不能保证主内存会被访问,只有缓存一致的值。i、 e.所有螺纹将看到相同的值 顺便说一句:你当然知道它比需要的复杂得多。你所需要的只是
public enum MySingleton {
INSTANCE;
}
做了几乎相同的事情。比赛条件如下:
instance==NULL
并正在运行此代码instance=new MySingleton()代码>。写入实例
是可见的,但写入MySingleton
还没有
实例!=NULL
并开始处理实例使
实例
易失性解决了这个问题,因为JDK内存规范(从JDK5开始)保证了对非易失性对象的写入不会与对易失性对象的写入顺序不一致。因此,任何看到实例!=NULL必须看到实例本身。竞争条件如下:
线程A看到instance==NULL
并正在运行此代码instance=new MySingleton()代码>。写入实例
是可见的,但写入MySingleton
还没有
线程B看到实例!=NULL
并开始处理实例
线程B现在正在处理一个它看不到其构造的对象
使实例
易失性解决了这个问题,因为JDK内存规范(从JDK5开始)保证了对非易失性对象的写入不会与对易失性对象的写入顺序不一致。因此,任何看到实例!=NULL
必须看到实例本身。可能重复您如何在没有阅读的情况下获得此代码?所有的解释都很容易理解。更多的解释请。。。我看到,由于线程在进入同步块本地缓存时是无效的,因此后续读取将直接进入主存,因此线程B应该从主存更新其实例值,而不需要是易失性的。可能重复的。您如何在没有读取的情况下获得此代码?所有的解释都很容易理解。更多的解释请。。。我看到,由于线程在进入同步块本地缓存时是无效的,因此后续读取将直接进入主存,因此线程B应该从主存更新其实例值,而不需要是易失性的,MySingleton的构造函数所做的写操作不能保证被线程B看到。但是线程B如何能看到实例!=当线程A没有释放同步锁(这意味着线程A没有将其缓存刷新到主内存)时,null。。因此线程B应该看到instance=null的旧值。写操作没有在缓存中锁定。缓存始终处于刷新状态,否则它将变满,来自CPU的写入将暂停。刷新缓存意味着确保缓存已刷新。不刷新它不会在里面粘任何东西。顺便说一句,实际硬件上涉及的缓存不是内存缓存。它是CPU的写投递缓冲区、读预取和指令重新排序。真正的缓存是100%SMP一致的。Java描述通常是关于一台没有缓存一致性但没有写投递缓冲区的假设机器。Java运行的真正机器与Java定义的虚构的理论机器不同。因此,一些Java没有定义为工作的东西总是可以工作的。假设Java机器中的某些故障模式在实际硬件上会出现故障,但情况不同。因此,我们需要volatile来确保指令的顺序相同。。。美好的非常好的回答,兄弟。。非常感谢您的帮助。具体来说,MySingleton的构造函数所做的写操作不保证线程B可以看到。但是线程B如何看到实例!=当线程A没有释放同步锁(这意味着线程A没有将其缓存刷新到主内存)时,null。。因此线程B应该看到instance=null的旧值。写操作没有在缓存中锁定。缓存始终处于刷新状态,否则它将变满,来自CPU的写入将暂停。刷新缓存意味着确保缓存已刷新。不刷新它不会在里面粘任何东西。顺便说一句,实际硬件上涉及的缓存不是内存缓存。它是CPU的写投递缓冲区、读预取和指令重新排序。真正的缓存是100%SMP一致的。Java描述通常是关于一台没有缓存一致性但没有写投递缓冲区的假设机器。Java运行的真正机器与Imagine不同