同步块的对象参数是否保证可见性 同步 Java

Are object parameters of synchronized block guranteed visibilty Synchronized Java

提问人:Mohammad Karmi 提问时间:10/29/2023 最后编辑:Mohammad Karmi 更新时间:10/30/2023 访问量:76

问:

对于以下情况,我找不到答案:

  public class Example {
    int a=0;

    public  synchronized void method(Object x){
        a++;
        x.value=x.value+1;
    }
}

我知道 happens-before 关系是为同一个对象锁建立的,所以所有内容都应该写入内存(但不确定这里的所有内容是否都包括对 x 的更改),

我的问题:如果对象 X 使用相同的锁,是否保证对其他线程可见?( lock 在示例对象上,但不在 x 本身上)

Java 多线程 同步 内存屏障

评论

1赞 Solomon Slow 10/30/2023
“一切”包括一切——甚至.锁除了它自己之外,什么都不。锁定同步块纯粹是建议性的x

答:

0赞 Stephen C 10/30/2023 #1

如果对象使用相同的锁,则是否保证对其他线程可见?x

是的,但这可能没有用。

首先,我需要将您的示例代码转换为可编译的内容,并可用于说明您的问题。

public class Example {
    int a = 0;

    public  synchronized void method(X x){
        a++;
        x.value = x.value+1;
    }
}

public class X {
    int value;
}   

然后,场景如下:

  1. 创建 和 的实例并将其传递给两个线程 A 和 B。ExampleShared

  2. 线程 A 使用公共实例进行调用。example.method(x)

  3. 一段时间后,线程 B 使用通用实例进行调用。example.method(x)

线程 A 在实例上释放互斥锁与线程 B 获取互斥锁之间将存在 happen before 关系。当我们将此与 happens before 关系相结合时,我们将得到一个保证Examplemethod

  • 任一线程 B 将看到 A 写入的变量的更新值value
  • 或者它将看到在那之后写入的另一个值。

此外,如果任何代码写入调用外部,或使用不同的实例作为锁,则所有赌注都关闭。并且不能保证如果直接读取另一个线程会看到什么。x.valuemethodExamplex.value


总之,有一些有保证的行为,但由于围绕保证的注意事项,很难安全地利用它们。最好执行以下操作之一:

  • 直接锁定实例;例如,通过 ) 上的同步方法,或者XincrementX
  • 隐藏实例,以便仅通过 的(例如)方法操作它们。XsynchronizedExample

评论

0赞 Mohammad Karmi 10/30/2023
只是为了留在问题的上下文中,发生在关系保证线程修改的所有对象的可见性之前,无论是锁还是任何其他对象,您能确认一下吗?
0赞 Stephen C 10/30/2023
它保证了对共享变量(而不是对象)的内存写入的可见性。正如我所描述的那样,这些保证是完整的,并带有警告。我不会用你正在使用的更宽松的术语来重述它们,因为它是模棱两可的。特别是在您的示例中......这也是如何不编写多线程代码的一个例子!
0赞 Stephen C 10/30/2023
我认为答案是“是”......但它可能是“不”......取决于你对可见性的理解。
0赞 Mohammad Karmi 11/1/2023
可见性意味着:A , B 是使用相同锁定对象的线程。如果线程 A 修改了 synchrnise 块内的任何对象,则块 B 应该对 A 修改的所有对象(包括锁定对象)具有可见性
0赞 Stephen C 11/1/2023
那太模糊/高层次了。实际保证是在读取和写入多个线程可访问的变量的级别。如果你要尝试做一些毛茸茸的事情,涉及访问同步块(或等效块)之外的共享变量,你必须在分析之前,在变量读取和写入的级别上执行这些操作。(国际海事组织)你不能走你正在尝试的心理捷径。但。。。坦率 地 说。。。你不应该这样写代码。它太脆弱/太难理解。