复合对象的同步方法如何工作

时间:2016-05-04 12:28:10

标签: java concurrency synchronization

我很好奇下一个代码片段是否是线程安全的,特别是关注带有复合对象的synchronized关键字。如果 updateAge 发生在 getB 之前,第二个来电者会收到更新的年龄值吗?

如果答案是肯定的,请解释JVM如何执行该操作? (我假设JVM代码在将synchronized方法/块退出到主内存时必须刷新访问的对象,JVM代码是从根对象中提取所有引用的对象吗?)

public class A {

    private B b;

    public B getB() { return b; }

    public void setB(B b) { this.b = b; }
}

public class B {

    private String name;
    private Integer age;

    public String getName() { return name; }

    public void setName(String name) { this.name = name; }

    public Integer getAge() { return age; }

    public void setAge(Integer age) { this.age = age; }
}

public class Main {

    private A a;

    public Main() {
        a = new A();
        B b = new B();
        b.setName("name");
        b.setAge(10);
        a.setB(b);
    }

    public synchronized void updateAge(Integer age){ a.getB().setAge(age); }

    public synchronized B getB() { return a.getB(); }
}

更新1:

替代类是否等同于上面的原始Main类?由于ConcurrentMap在put期间执行同步。忽略大小写是2个并发线程调用 updateAge 方法。

public class Main2 {

    private ConcurrentMap<String, A> store = new ConcurrentHashMap<>();


    public Main2() {
        A a = new A();
        B b = new B();
        b.setName("name");
        b.setAge(10);
        a.setB(b);

        store.put("id", a);
    }

    public void updateAge(Integer age){
        A a = store.get("id");
        a.getB().setAge(age);
        store.put("id", a);
    }

    public B getB() { return store.get("id").getB(); }
}

1 个答案:

答案 0 :(得分:1)

这取决于线程安全的含义。如果系统只包含这两种方法,那么答案是肯定的,这是线程安全的,因为updateAge()的任何更改对于getB()的调用者都是可见的。

但是,由于getB()返回B的可变实例,所以没有什么可以阻止我写这样的内容:

Main main = ...;
main.updateAge(42); // we change the age of B in a synchronized block
B myLittleB = main.getB(); //this is synchronized to the same object, so it's all fine
myLittleB.setName("Boaty McBoatface"); //this isn't synchronized so if another thread calls main.getB().getName(), all bets are off

更新:如何实现可见性保证取决于VM实施和体系结构,但有几种可用的替代策略,例如运行时代码分析以确定哪些变量可能在同步块中发生更改,或者甚至不加歧视地冲洗一切。