【问题标题】:How AtomicReference use CAS in case of Complex Object在复杂对象的情况下,AtomicReference 如何使用 CAS
【发布时间】:2015-10-14 03:24:47
【问题描述】:

AtomicReference 实例使用不安全的 CAS 操作来利用处理器指令进行锁定。但我有点困惑它在复杂对象的情况下是如何工作的。

例如,假设我有一个 Person 类的实例(id、firstName、lastName)。我将对象实例共享给多个线程 t1、t2 和 t3。由于操作应该是原子的,所以我不会共享 Person 类对象实例,而是将该对象包装到 AtomicReference 实例中并与所有线程共享。

现在线程 t1 只更新 firstName,线程 t2 只更新 lastName,线程 t3 更新 firstName 和 lastName。之后每个线程都会调用 compareAndSet 方法来反映新的变化。

我还使用了 volatile 引用,以便写入可以发生在主内存中并且对所有线程可见。

我想明白:

  1. 在上述场景中,当 compareAndSet 被调用时,Person 类实例的预期值和新值之间会比较哪些内容(例如 id、firstName、lastName)?

  2. 假设线程 t1 已更新 firstName 并调用 compareAndSet。线程 t2 更新了 lastName 并且将调用 compareAndSet。在这种情况下,AtomicReference 如何保证线程 t2 不会删除线程 t1 所做的更改,即更新 firstName?

  3. 假设 2 个线程 t1 和 t2 同时调用 compareAndSet,那么谁会赢得比赛,而其他输了的线程会发生什么?

【问题讨论】:

    标签: java multithreading atomicreference


    【解决方案1】:

    如果你共享了可变的Person 对象,AtomicReference 根本不会帮助你。您需要使Person 不可变,并在每次要应用更改时创建一个新的Person。这样,您的线程更新多少字段都没有关系。所以假设你有这样不可变的Person 类:

    public class Person {
        public final int id;
        public final String firstName, lastName;
    
        public Person(int id, String firstName, String lastName) {
            this.id = id;
            this.firstName = firstName;
            this.lastName = lastName;
        }
    }
    

    那么您的线程可能会执行以下操作

    AtomicReference<Person> ref; // shared reference
    
    public void updateFirstName(String firstName) {
        Person curPerson, newPerson;
        do {
            curPerson = ref.get();
            newPerson = new Person(curPerson.id, firstName, curPerson.lastName);
        } while (!ref.compareAndSet(curPerson, newPerson));
    }
    
    public void updateLastName(String lastName) {
        Person curPerson, newPerson;
        do {
            curPerson = ref.get();
            newPerson = new Person(curPerson.id, curPerson.firstName, lastName);
        } while (!ref.compareAndSet(curPerson, newPerson));
    }
    
    public void updateName(String firstName, String lastName) {
        Person curPerson, newPerson;
        do {
            curPerson = ref.get();
            newPerson = new Person(curPerson.id, firstName, lastName);
        } while (!ref.compareAndSet(curPerson, newPerson));
    }
    

    调用此类方法,您将整体更新Person,并且不会有任何竞争条件。

    至于你的第三个问题,并没有指定谁会赢,但是失败的线程只会再迭代一次并相应地更新另一个字段,所以你最终会更新两个字段。

    【讨论】:

      【解决方案2】:

      AtomicReference 的全部意义在于表示

      可以自动更新的对象引用。

      它不会阻止您以原子方式修改引用指向的对象。

      在上述场景中,当compareAndSet 被调用时,是什么情况 将在 Person 类的预期值和新值之间进行比较 实例(例如 id、firstName、lastName)?

      这些都不是。 compareAndSet 将验证当前引用是否等于expected 引用,使用引用相等,即。 ==。来自 javadoc

      如果当前的值自动设置为给定的更新值 value == 期望值。

      它不知道也不关心引用的类型,因此不知道也不关心它的成员或定义它们的逻辑。

      假设 2 个线程 t1 和 t2 同时调用compareAndSet 那么 谁将赢得比赛,其他线程谁输了怎么办?

      订单未定义。 compareAndSet 仅在当前参考等于给定的预期参考时才有效。该方法将返回

      true 如果成功。 falsereturn 表示实际值为 不等于预期值。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-01-14
        • 1970-01-01
        • 2012-03-31
        • 2011-05-06
        • 2014-08-25
        • 2017-12-15
        • 2021-08-25
        相关资源
        最近更新 更多