代码之家  ›  专栏  ›  技术社区  ›  Pablo Fernandez

Java并发——为什么不同步一个SETER(而不是一个吸气剂)使类线程安全呢?[复制品]

  •  6
  • Pablo Fernandez  · 技术社区  · 14 年前

    可能重复:
    Thread safety in Java class

    我在读书 实践中的Java并发 我来举一个让我困惑的例子。

    作者声明这个类不是线程安全

    public class MutableInteger {
    
        private int number;
    
        public int getInt() {
            return number;
        }
    
        public void setInt(int val) {
            number = val;
        }
    }
    

    它们还指出,只同步一个方法(例如setter)是不行的;您必须同时同步这两个方法。

    我的问题是:为什么?同步二传手就不行了吗?

    4 回复  |  直到 9 年前
        1
  •  5
  •   Michael Barker    9 年前

    Java在内存模型发生之前发生了一个事件。在写路径和读路径上都需要一些通用的并发构造(例如,synchronized block/method、lock、volatile、atomic)来触发这种行为。

    如果同步这两个方法,则会在整个对象上创建一个锁,该锁将由读线程和写线程共享。JVM将确保在离开(synchronized)setint方法之前在写入线程上发生的任何更改在进入(synchronized)getint方法之后对任何读取线程都可见。JVM将插入必要的内存屏障,以确保发生这种情况。

    如果只同步了写入方法,则对对象的更改可能对任何读取线程都不可见。这是因为在读取路径上没有一个点是JVM可以用来确保读取线程的可见内存(缓存等)与写入线程一致的。使getint方法同步将提供此功能。

    注意:特别是在这种情况下,使字段“number”变为volatile将提供正确的行为,因为volatile读/写在JVM中也提供相同的内存可见性行为,setint方法内部的操作只是一个赋值。

        2
  •  5
  •   Soundlink    14 年前

    在样本前的书中有解释(第35页):

    “仅同步setter是不够的:调用get的线程仍然可以看到过时的值。”

    过时的数据:当读线程检查就绪时,它可能会看到一个过期的值。除非使用同步 每次访问变量时 ,可以看到该变量的过时值。更糟糕的是,过时不是全部或全部:线程可以看到一个变量的最新值,但可以看到另一个首先写入的变量的过时值。

        3
  •  1
  •   Wei S    14 年前

    如果只同步setter方法,则只能保证属性不会被错误地修改,但在尝试读取变量时不能确保它是过时的值。

        4
  •  -1
  •   Bwmat    14 年前

    因为 number 不易挥发,并且 getInt() 不同步, GETIN() 可能返回过时的值。有关更多信息,请阅读有关Java内存模型的内容。