在对象Y上定义的同步块内部访问对象X是否安全?

huangapple go评论93阅读模式
英文:

Is it safe to access an object X within a synchronized block defined on object Y?

问题

以下是翻译好的内容:

在同步方法或块内访问对象的状态是否安全,这个问题是有答案的。我的问题是:在一个同步块内访问对象X是否安全,其中同步块是在另一个对象Y上,且存在多个写入线程用于X?

public class X{
    private int value = 0;
    /** 多个线程将调用set方法 **/
    public void set(int v){
        this.value = v;
    }
    public int value(){
        return value;
    }
}

public class Tester{
    private final Object Y = new Object();
    public void test(X x){
        synchronized(Y){
            System.out.println(x.value()); // 是否保证从内存中读取x.value,而不是从当前线程的缓存中读取?
        }
    }
}

我知道在定义同步块的对象上访问对象的状态是安全的,无需使用volatile,但如果同步块是在另一个对象上定义的呢?

英文:

There are answers to the question if accessing the state of the object is safe within the synchronized method or block. My question : Is it safe to access an object X within a synchronized block, where synchronized block is on another object Y and there are multiple writers Threads present for X ?

public class X{
    private int value = 0;
    /** set method will be invoked by multiple threads**/
    public void set(int v){
       this.value = v;
   }
   public int value(){
       return value;
   }
}

public class Tester{
    private final Object Y = new Object();
    public void test(X x){
        synchronized(Y){
            System.out.println(x.value()); // is it guaranteed that x.value will be read from memory and not from the current thread's cache ?
        }
    }
}      

I know state of the object on which synchronized block is defined is safe to access without a need for volatile, but what if the synchronized block is defined on another object ?

答案1

得分: 3

在我看来,你正在提出错误的问题。Java内存模型并不涉及对象和类本身,它只关注于变量的可见性。

对于synchronized(o)块的可见性规则非常简单:无论一个线程在离开synchronized(o)块之前对任何变量做了什么,都保证在另一个随后进入以相同实例o为锁的块的线程中对其他线程可见。

因此,在你的示例中,如果有一个X my_x,并且某个线程A执行了以下操作:

synchronized(Y) {
    my_x.set(55);
}

那么当另一个线程B随后调用tester.test(my_x)时,线程B将看到线程A存储的值。

另一方面,如果线程A在没有对Y同步的情况下调用my_x.set(...),那么Java不保证线程B何时、是否会看到这个变化。


注意:你的程序通过将锁对象Y设为Tester类的private成员,同时将test(X)函数设为public,公开地引发了失败的可能性。这几乎在呼唤你(或其他程序员)犯下一个错误,即调用tester.test(some_random_X),而some_random_X是由另一个没有锁定Y的线程设置的。

英文:

IMO you are asking the wrong question. The Java Memory Model doesn't concern itself with objects and classes. It only talks about the visibility of variables.

The visibility rule for synchronized(o) blocks is pretty simple: Whatever one thread does to any variable before it leaves a synchronized(o) block is guaranteed to be visible to any other thread after the other thread subsequently enters a block that is synchronized on the same instance, o.

So, in your example, if you have some X my_x, and some thread A does this:

synchronized(Y) {
    my_x.set(55);
}

Then when some other thread B subsequently calls tester.test(my_x), thread B will see the value that was stored by thread A.

On the other hand, if thread A calls my_x.set(...) without synchronizing on Y, then Java does not promise when, if ever, the thread B will see the change.


Note: Your program openly invites failure by making the lock object, Y, a private member of the Tester class, while making the test(X) function public. That practically begs you (or some other programmer) to make the mistake of calling tester.test(some_random_X) where the some_random_X was set by another thread that did not lock Y.

答案2

得分: -3

不,这是不安全的。

与问题无关:

示例代码的第一个问题:

public void set(int v){
    this.value = v;
}

这是极差的代码质量,应该像这样编写:

public final void setValue(final int value) {
    this.value = value;
}

这没有性能优势,除非添加final修饰符可能会改进字节码优化,它只是一个设置器的设计模式。

示例代码的第二个问题:

public int value(){
    return value;
}

正确的获取器设计模式是:

public final int getValue() {
    return value;
}

示例代码的第三个问题:

private final Object Y = new Object();

字段 'Y' 不是静态的,因此不是常量,对于此字段的正确命名约定应为:

private final Object y = new Object();

示例代码的第四个问题是在括号后面加上花括号,没有空格,我的意思是,而不是这样做:

){

你真正应该做的是:

) {
英文:

No it is not safe.

Unrelated to the question:

First problem with the example code:

public void set(int v){
    this.value = v;
}

this is extremely poor code quality and should really be written like this:

public final void setValue(final int value) {
    this.value = value;
}

this has no performance benefit other than addition of the final modifiers may improve bytecode optimization it is just a design pattern for setters.

Second problem with the example code:

public int value(){
    return value;
}

the correct design pattern for a getter is:

public final int getValue() {
    return value;
}

Third problem with the example code:

private final Object Y = new Object();

the field 'Y' is not static and therefore is not a constant, the correct naming convention for this field would be:

private final Object y = new Object();

The forth problem with the example code is the putting braces after parentheses without a space, what i mean by that is instead of doing

){

you should really be doing:

) {

huangapple
  • 本文由 发表于 2020年5月3日 18:45:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/61573152.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定