当前位置: 首页 > 知识库问答 >
问题:

Java中同步关键字的记忆效应

欧阳学真
2023-03-14

这个问题以前可能已经回答过了,但是由于这个问题的复杂性,我需要一个确认。所以我重新措辞这个问题

问题1:当一个线程进入一个同步块时,内存屏障将包括被触摸的任何字段,而不仅仅是我同步的对象的字段?因此,如果在一个同步块中修改了许多对象,那么在线程内存缓存之间会有大量内存移动。

Thread 1
object.field1 = "";
synchronized (lock) {
  farAwayObject.field1 = "";
  farAwayObject.evenFarther.field2 = "";
}

Thread 2. assuming thread ordering is correct
synchronized (lock) {
  //thread 2 guaranteed to see all fields above as ""
  //even object.field1 ?
}

问题 2 : object.field1 = “”;在线程 1 中隐式地是“发生前”关系的一部分?

我希望是这样,但可能不是这样。如果没有,有没有一个技巧可以让它这样做,而不将其放入同步块?否则很难对程序进行推理,并且将所有内容都放在同步{}下是不切实际的。

编辑:澄清:object.field1不是易失性的,问题是“线程2是否保证看到线程1的写入,至少”。我的问题是关于内存可见性的。为了这个参数,假设只有线程 1 写入非易失性 object.field1。

问题2可以改写为

“锁上的同步块是否会推动之前所做的更改,以便被在同一锁上同步的其他线程看到?

共有3个答案

诸葛柏
2023-03-14

内存屏障将包括所触及的任何字段,而不仅仅是我同步的对象的字段?

是的。

Is object.field1 =在线程1中隐含部分发生前关系?

是的,即使它没有挥发性。

发生在顺序之前的是偏序。

先发生顺序是由同步的传递闭包给出的,带有边和程序顺序。它必须是一个有效的部分顺序:自反、传递和反对称。

(JLS 17.4.7)

同步边缘之前的操作(即同步锁的释放)按程序顺序排序,因此与释放同步。传递性说,由另一个线程中相同锁的获取排序的操作因此具有发生之前的顺序,即释放该锁和释放该锁之前的操作,无论它是否在同步块的主体内。要记住的重要一点是,排序发生在操作(即获取/释放锁)上,而不是像同步关键字的括号所暗示的块。括号表示获取/释放操作的位置以及一组操作无法交错的位置。

最后,请记住“之前发生”是一个“部分”顺序。它的意思是:

  1. 在操作碰巧以特定顺序(即释放/获取、写入/读取等)出现时强制实施内存一致性
  2. 之前发生的事情依赖于更强的保证,例如程序顺序来产生正确的功能。
  3. 之前发生并不能防止非原子动作交错产生的错误
  4. 发生在操作和强操作之间的传递关系之前(您可以将共享变量的读取放在同步块之外,只要写入在块之前,读取在块之后)。在订购之前,也会发生更强的排序保证,但它们还提供了规范中描述的其他效果。
曹华荣
2023-03-14

当线程进入同步块时,内存屏障将包括任何接触的字段,而不仅仅是我同步的对象的字段

假设farAwayObjectevenFarther的字段始终通过在您的应用程序周围的同一对象上获取lock来修改和访问,所有线程将始终看到对farAwayObjectevenFarther所做的更新,因为同步强制执行发生前条件。

//线程2保证将上面的所有字段视为“”

对于 object.field1 来说,在不知道它是如何声明的的情况下,不能这么说。假设 field1 是未标记为易失性的引用,则在关系和线程可能会看到它的过时值之前,它不会是发生的一部分。

我希望是,但可能不是。如果不是,有没有一个技巧可以让它不进入同步块?

是的。将 object.field1 标记为易失性

添加您的编辑:

问题2可以改写为

“锁上的同步块是否会推动之前所做的更改,以便被在同一锁上同步的其他线程看到?

AFAIK答案是是,前提是写入线程在读取线程之前获得锁。不幸的是,这通常是您无法保证的,这就是为什么object.field1需要标记为易失性或语句需要移动到同步块内。

看看 JSR 133,它讨论了当线程退出同步块时,缓存被刷新到主内存。这应该进一步澄清一些事情。

高正初
2023-03-14

1) 当一个线程进入一个同步块时,内存屏障将包括任何被触摸的字段,而不仅仅是我同步的对象的字段?

正确。(假设线程1和线程2在同一个锁上同步。)

因此,如果在同步块内修改了许多许多对象,则线程内存缓存之间的大量内存移动。

可能是的。但是,它(可能)不是在缓存之间移动。更有可能的是,它是从一个处理器的缓存移动到内存,以及从内存到第二个处理器的缓存的移动。当然,这取决于硬件如何实现内存层次结构。

2)Is object . field 1 = " ";在线程1中隐含部分发生前关系?

这是一连串发生之前的关系

  1. object.field1的写入发生在获取锁之前。
  2. 这发生在写入farAwayObject等等之前。
  3. 这发生在线程1释放锁之前
  4. 这发生在线程2获取锁之前
  5. 这发生在线程2读取object.field1之前。

问题是,如果在线程1或其他线程获取< code>lock之前,存在对< code>object.field1的干预写入,会发生什么情况。在这两种情况下,发生之前链都不足以确保线程2看到线程1写入的值。

 类似资料:
  • 本文向大家介绍Java中的同步关键字,包括了Java中的同步关键字的使用技巧和注意事项,需要的朋友参考一下 当我们在程序中启动两个或多个线程时,可能会出现多个线程尝试访问同一资源,最终由于并发问题,它们可能产生无法预料的结果的情况。例如,如果多个线程试图在同一文件中写入数据,则它们可能会破坏数据,因为其中一个线程可以覆盖数据,或者一个线程同时打开同一文件时,另一个线程可能正在关闭同一文件。 因此,

  • 问题内容: c#是否具有自己的java“ synchronized”关键字版本? 即在Java中,可以将其指定为函数,对象或代码块,如下所示: 要么 问题答案: 首先-大多数类将永远不需要是线程安全的。使用YAGNI:仅当你知道实际上将要使用它(并对其进行测试)时,才应用线程安全性。 对于方法级的东西,有: 这也可以用于访问器(属性和事件): 请注意,默认情况下,类似字段的事件是同步的,而自动实现

  • 问题内容: 所以我正在用关键字测试。这是我尝试的示例: 当我运行它时,来自两个线程的调用方法的输出生成以下输出: 当我将方法更改为: 我得到以下输出: 尽管这使我清楚地了解的目的,但我想知道还有其他可以使用的原因。还是我在这里所做的,是为什么我们需要使用此关键字的唯一原因? 谢谢。 编辑: 令我感到困惑的另一件事是,在第一个输出中,为什么计数器在7之后变为3。这对我来说似乎有点不可能,但是每次尝试

  • 问题内容: 假设我有以下Java代码 同步是否会阻止重新排序?a,b和c之间没有依赖关系。先分配给b然后再分配给c?如果我还没有同步,则可以用JVM选择的任何方式对语句重新排序? 问题答案: 同步是否会阻止重新排序? 它可以防止重新排序。您仍然可以在同步块外部和同步块内部进行重新排序,但不能从同步块内部对其进行重新排序。 a,b和c之间没有依赖关系。 没关系。 先分配给b然后再分配给c? 是。但是

  • 问题内容: C#中 var 关键字的一种用法是隐式类型声明。 var 的Java等效语法是什么? 问题答案: 空无一人。las,您必须输入完整的类型名称。 编辑:发布7年后,Java 10中添加了局部变量的类型推断(带有)。 编辑:发布6年后,从下面收集一些评论: C#使用关键字的原因是因为在.NET中可能有没有名称的Types。例如: 在这种情况下,不可能给赋予正确的类型。6年前,这在Java中

  • 本文向大家介绍Java中synchronized关键字修饰方法同步的用法详解,包括了Java中synchronized关键字修饰方法同步的用法详解的使用技巧和注意事项,需要的朋友参考一下 Java的最基本的同步方式,即使用synchronized关键字来控制一个方法的并发访问。 每一个用synchronized关键字声明的方法都是临界区。在Java中,同一个对象的临界区,在同一时间只有一个允许被访