AtomicReference 如何在复杂对象的情况下使用 CAS

Sha*_*kar -1 java multithreading atomicreference

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

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

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

此外,我正在使用易失性引用,以便写入可以发生在主内存中并对所有线程可见。

我想明白:

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

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

  3. 假设 2 个线程 t1 和 t2 同时调用 compareAndSet 那么谁将赢得比赛,而其他失败的线程会怎样?

Tag*_*eev 5

如果您共享了可变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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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));
}
Run Code Online (Sandbox Code Playgroud)

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

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