【问题标题】:Synchronization on an object, then on that object's field在对象上同步,然后在该对象的字段上同步
【发布时间】:2015-04-03 08:06:22
【问题描述】:

有多个线程访问一个对象。为了避免内存一致性错误,我对该对象使用了同步方法。
这是否意味着当我在该对象上同步时,只有对象的字段被同步(而不是对象的字段的字段)? 例如:

public class Class1 {

    private Object value1;

    public Object getValue1() {
        return this.value1;
    }

    public void setValue1(Object value1) {
        this.value1 = value1;
    }

}

哪个代码是正确的(Class2Class3),为什么?

public class Class2 {

    private final Class1 object1 = new Class1();

    private Object value2;

    public synchronized void setValues(Object value1, Object value2) {
        object1.setValue1(value1);
        this.value2 = value2;
    }

    public synchronized Object[] getValues() {
        return new Object[] { object1.getValue1(), this.value2};
    }

}

public class Class3 {

    private final Class1 object1 = new Class1();

    private Object value2;

    public synchronized void setValues(Object value1, Object value2) {
        synchronized (object1) {
            object1.setValue1(value1);
        }
        this.value2 = value2;
    }

    public synchronized Object[] getValues() {
        Object value1;
        synchronized (object1) {
            value1 = object1.getValue1();
        }
        return new Object[] { value1, this.value2};
    }

}

更具体:

Class2 obj = new Class2();

// thread 1
obj.setValues(..., ...);

// thread 2
Object[] values = obj.getValues();

由于setValuesgetValues 都是同步的,所以this.value2 = value2;return new Object[] {..., this.value2};Class2 中将存在happens-before 关系。
但是object1 呢?不能保证this.value1 = value1;Class1 中发生在return this.value1; 之前。不是吗?

【问题讨论】:

    标签: java multithreading synchronization synchronized


    【解决方案1】:

    它们都是正确的,但这只是因为您使用它们的具体方式(除了您正在创建的实例的方法之外,没有什么可以使用object1value2,因为object1 或@ 987654324@ 和实例专用)。您的主要问题的答案:

    这是否意味着当我在该对象上同步时,仅同步对象的字段(而不是对象字段的字段)?

    ..是,仅访问该对象的字段,而不是其字段的字段,是同步的。

    这是一个可以实际证明问题的示例:

    class Foo {
        Class1 c1;
    
        Foo(Class1 c) {
            this.c1 = c;
        }
    
        synchronized Object getValue1() {
            return c1.getValue1();
        }
    
        synchronized void setValue1(Object newValue) {
            c1.setValue1(newValue);
        }
    }
    

    这样,Foo 的两个实例可能共享Class1相同实例:

    Class1 c1 = new Class1();
    Foo f1 = new Foo(c1);
    Foo f2 = new Foo(c2);
    

    ...由于同步仅同步对Foo 实例的访问,因此您可能对他们都在使用的Class1 实例具有非同步访问权限。

    【讨论】:

    • 那么在Class2中访问object1value1时不会出现内存一致性错误?为什么? object1value2 都是引用字段,并且只有这些字段的值(即对象在内存中的地址)是同步的。 value2 很好,因为我们不访问它自己的状态。但是object1value1 呢?是否有可能获得“旧”值而不是新值?为什么?
    • @Victor:你不同步字段;你同步线程。同样:您的示例有一个 private Class 实例,并且对该实例的唯一访问是通过在包含它的实例上同步的同步方法。因此,在任何给定时间,只有一个线程可以使用这些方法,而您不必进行进一步的同步。但这只是因为您的类有一个其他人无法访问的私有实例。不要考虑同步字段,考虑使用这些字段的线程
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-16
    相关资源
    最近更新 更多