【问题标题】:What should be synchronized with Java objects什么应该与 Java 对象同步
【发布时间】:2012-03-19 19:56:52
【问题描述】:

假设您有 2 个或更多线程共享一个对象 A。

对象 A 内部有一个列表,其中包含另一个对象 B

对象 A 列表 对象B

现在线程修改/读取对象 B

问题是,应该同步什么? 对象 A,因为他们都访问它,或者对象 B,因为这是他们读取/修改的内容

我似乎无法在任何地方找到我的问题的答案。

提前致谢

【问题讨论】:

    标签: java concurrency synchronized


    【解决方案1】:

    走一条最简单、最安全的路线,您需要在对象层次结构中最外层的对象上进行同步,该对象应以原子方式操作。

    在您的示例中,您似乎需要在 一个列表 上进行同步(不幸的是,如果没有实际代码,我无法确定您到底要做什么)。

    这里是示例代码:

    class B {
    }
    
    class A {
        private final List<B> listOfBs = new ArrayList<B>();
    
        void add(B b) {
            synchronized (listOfBs) {
                listOfBs.add(b);
            }
        }
    
        List<B> getSnapshot() {
            List<B> copy = new ArrayList<B>();
            synchronized (listOfBs) {
                copy.addAll(listOfBs);
            }
            return copy;
        }
    }
    

    请注意,您需要将列表声明为final,以消除某些代码可能会更改对违反原子性的列表的引用:

        // will make add() and getSnapshot() not mutually exclusive, thus violating atomicity of operations 
        ...
        listOfBs = new ArrayList<B>();
        ...
    

    阅读Java concurrent programming tutorial 会有所帮助。

    【讨论】:

    • 这段代码不是线程安全的,因为使用不可修改的包装器包装 listOfBs not 使得 getSnapshot() 的结果是线程安全的。 (因为 add() 的调用者可以修改不可修改的包装器引用的 listOfBs)。要使此代码线程安全,您必须在包装之前执行 复制 列表之类的操作,或者使用线程安全的集合实现。
    • getSnapshot() 中只有copy.addAll(listOfBs); 需要在同步块中。最好限制同步的范围。
    • 谢谢,这很有帮助我感到困惑的部分:2+线程访问对象A是否有任何问题?
    • @Remco 不,一旦您的代码始终适用于 2 个线程,它将适用于任意数量的线程。
    【解决方案2】:

    如果您通过对象 B 的一种方法修改对象 B,那么您应该使该方法(或执行写入的部分)同步。

    【讨论】:

      猜你喜欢
      • 2012-09-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-29
      • 2011-07-22
      • 2014-03-11
      相关资源
      最近更新 更多