Java 同步方法与复合对象的工作原理

Java 同步方法与复合对象的工作原理,java,concurrency,synchronization,Java,Concurrency,Synchronization,我很好奇下一个代码片段是否是线程安全的,特别是关注带有复合对象的synchronized关键字。如果updateAge发生在getB之前,第二个调用方是否会收到更新的age值 如果答案是肯定的,请解释JVM是如何执行的?(我假设JVM代码在退出同步方法/块到主存时必须刷新访问的对象,JVM代码是否从根对象提取所有引用的对象?) 更新1: 替代类是否等同于上述原始主类?因为ConcurrentMap在put期间执行同步。忽略2个并发线程调用updateAge方法的情况 公共类Main2{ 私有Co

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

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

更新1:

替代类是否等同于上述原始主类?因为ConcurrentMap在put期间执行同步。忽略2个并发线程调用updateAge方法的情况

公共类Main2{
私有ConcurrentMap存储=新ConcurrentHashMap();
公共事务2(){
A=新的A();
B=新的B();
b、 设置名称(“名称”);
b、 设置(10);
a、 挫折(b);
存储。放置(“id”,a);
}
公共void updateAge(整数年龄){
A=store.get(“id”);
a、 getB().setAge(年龄);
存储。放置(“id”,a);
}
公共B getB(){return store.get(“id”).getB();}
}

这取决于线程安全性的含义。如果系统只包含这两个方法,那么答案是肯定的,这是线程安全的,因为调用
getB()
的调用方可以看到
updateAge()
所做的任何更改

但是,由于
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
更新:可见性保证的实现方式取决于虚拟机的实现和体系结构,但有几种替代策略可用,例如运行时代码分析,以确定哪些变量可能会在同步块中发生更改,甚至可以不加区别地刷新所有内容

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(); }
}
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