Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/339.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 更喜欢同步而不是易失性_Java_Multithreading_Synchronization_Volatile - Fatal编程技术网

Java 更喜欢同步而不是易失性

Java 更喜欢同步而不是易失性,java,multithreading,synchronization,volatile,Java,Multithreading,Synchronization,Volatile,我读了这篇文章,最后写了以下内容: 使用volatile可以做的任何事情都可以通过synchronized完成,但是 反之亦然 不清楚。定义易失性字段如下: 字段可以声明为volatile,在这种情况下,Java内存模型 确保所有线程都能看到变量的一致值 (§17.4) 所以,volatile字段是关于内存可见性的。另外,根据我引用的答案,对volatile字段的读写是同步的 同步反过来保证只有一个线程可以访问同步块。正如我所得到的,它与内存可见性无关。我错过了什么?错了。同步与内存可见性有关。

我读了这篇文章,最后写了以下内容:

使用volatile可以做的任何事情都可以通过synchronized完成,但是 反之亦然

不清楚。定义易失性字段如下:

字段可以声明为volatile,在这种情况下,Java内存模型 确保所有线程都能看到变量的一致值 (§17.4)

所以,volatile字段是关于内存可见性的。另外,根据我引用的答案,对volatile字段的读写是同步的


同步反过来保证只有一个线程可以访问同步块。正如我所得到的,它与内存可见性无关。我错过了什么?

错了。同步与内存可见性有关。每个线程都有自己的缓存。如果你有一个锁,缓存是refresehd。如果释放一个锁,缓存将流入主内存


如果读取易失性字段,也会刷新,如果写入易失性字段,则会刷新。

事实上,同步也与内存可见性有关,因为JVM在同步块的出口添加了一个内存屏障。这确保了在第一个线程退出同步块后,另一个线程的读取将确保同步块中线程的写入结果可见

注意:在@PaŭloEbermann的评论之后,如果另一个线程通过读取内存障碍(例如通过进入同步块),它们的本地缓存不会失效,因此它们可能会读取旧值

在本文档中,同步块的退出发生在
之前:

寻找这些摘录:

一个线程写入的结果保证对一个线程可见 仅当写入操作发生在 读取操作

监视器的解锁(同步块或方法退出) 在每个后续锁(同步块或方法)之前发生 同一监视器的输入)。因为这种关系发生在关系之前 是可传递的,线程在解锁之前的所有动作 在任何线程锁定之后的所有操作之前发生 监视器


Synchronized和volatile是不同的,但通常它们都用于解决相同的常见问题

Synchronized是确保在给定时间点只有一个线程访问共享资源

然而,这些共享资源通常被声明为不稳定的,这是因为,如果一个线程更改了共享资源值,那么它也必须在另一个线程中更新。但如果没有volatile,运行时只需通过从缓存中读取值来优化代码。所以volatile所做的是,每当任何线程访问volatile时,它都不会从缓存中读取值,而是从实际内存中获取值,并使用相同的值


正在检查log4j代码,这就是我发现的

/**
 * Config should be consistent across threads.
 */
protected volatile PrivateConfig config;

如果多个线程写入一个共享的volatile变量,并且它们还需要使用该变量的前一个值,则可以创建一个。因此,此时您需要使用同步

。。。如果两个线程同时读取和写入共享变量,那么仅使用volatile关键字是不够的。在这种情况下,您需要使用synchronized来保证变量的读写是原子的。读取或写入volatile变量不会阻止线程读取或写入。要做到这一点,您必须在关键部分周围使用synchronized关键字


有关volatile的详细教程,请参阅。

进入和退出
同步的
块将执行内存屏障,确保更改在线程之间可见。回答非常好,非常感谢。我不认为在内存可视性概念下有更一般的概念。我的理解略有不同:同步块中的更改(实际上也是之前的更改)仅对以后在同一对象上同步的其他线程可见(或者以其他方式处于“发生在”关系中),并非所有其他线程的所有读取。@PaŭloEbermann确实,如果其他线程不同步(或以任何方式验证读取内存障碍),它们的本地缓存将不会失效,因此它们可能读取旧值。@PaŭloEbermann:不确定如何清晰准确地写下它。如果您对此有更好的解释,请编辑issue@St.Antario,当您阅读此答案时,请记住“之前发生”在Java语言规范(JLS)中具有特殊含义。当规范说“A发生在B之前”时,意味着A保证发生在B之前。因此,当他们说,“X只有在写入操作发生在读取之前时才是真的”,它们并不意味着如果写操作确实发生在读操作之前,那么X将为真:它们意味着只有当写操作保证发生在读操作之前,X才能保证为真。您可以使用
synchronized
块来保证一个线程中的写入发生在另一个线程中的读取之前。对于线程安全计数器或cas这样的简单情况,使用类型通常比
synchronized
块更容易,性能更好,尤其是在高争用率下。那么为什么要使用“volatile”关键字?您可以在不指定volatile或synchronized的情况下获得多线程访问。缓存始终是一致的。并且不需要刷新主内存。