6

假设您有以下课程

public class AccessStatistics {
  private final int noPages, noErrors;
  public AccessStatistics(int noPages, int noErrors) {
    this.noPages = noPages;
    this.noErrors = noErrors;
  }
  public int getNoPages() { return noPages; }
  public int getNoErrors() { return noErrors; }
}

然后执行以下代码

private AtomicReference<AccessStatistics> stats =
  new AtomicReference<AccessStatistics>(new AccessStatistics(0, 0));

public void incrementPageCount(boolean wasError) {
  AccessStatistics prev, newValue;
  do {
    prev = stats.get();
    int noPages = prev.getNoPages() + 1;
    int noErrors = prev.getNoErrors;
    if (wasError) {
      noErrors++;
    }
    newValue = new AccessStatistics(noPages, noErrors);
  } while (!stats.compareAndSet(prev, newValue));
}

在最后一行中,该方法while (!stats.compareAndSet(prev, newValue))如何确定和之间的相等性?类是实现方法所必需的吗?如果不是,为什么?javadoc 声明如下compareAndSetprevnewValueAccessStatisticsequals()AtomicReference.compareAndSet

如果当前值 == 预期值,则自动将值设置为给定的更新值。

...但是这个断言似乎很笼统,我在 AtomicReference 上阅读的教程从未建议为包装在 AtomicReference 中的类实现 equals()。

如果需要封装在 AtomicReference 中的类来实现 equals(),那么对于比AccessStatistics我想象的更复杂的对象,同步更新对象而不使用 AtomicReference 的方法可能会更快。

4

4 回答 4

6

它比较引用就像您使用 == 运算符一样。这意味着引用必须指向同一个实例。不使用 Object.equals()。

于 2009-12-08T21:33:01.707 回答
2

实际上,它不比较prev 和 newValue!

相反,它将存储在 stats 中的值与 prev 进行比较,只有当它们相同时,它才会将 stats 中存储的值更新为 newValue。如上所述,它使用等于运算符 (==) 来执行此操作。这意味着只有当 prev 指向存储在 stats 中的相同对象时,才会更新 stats。

于 2011-12-12T14:03:42.900 回答
0

它只是检查对象引用相等性(又名 ==),因此如果 AtomicReference 持有的对象引用在您获得引用后发生了更改,它不会更改引用,因此您必须重新开始。

于 2009-12-08T21:31:57.217 回答
0

以下是 AtomicReference 的一些源代码。AtomicReference 指的是对象引用。此引用是 AtomicReference 实例中的 volatile 成员变量,如下所示。

private volatile V value;

get() 只是返回变量的最新值(就像 volatiles 以“发生在之前”的方式一样)。

public final V get()

以下是 AtomicReference 最重要的方法。

public final boolean  compareAndSet(V expect, V update) {
        return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}

compareAndSet(expect,update) 方法调用 Java 的 unsafe 类的 compareAndSwapObject() 方法。这个不安全的方法调用调用了本地调用,它调用了一条到处理器的指令。“期望”和“更新”每个都引用一个对象。

当且仅当 AtomicReference 实例成员变量“value”引用同一个对象时,“expect”引用,“update”现在分配给该实例变量,并返回“true”。否则,返回 false。整个事情是原子完成的。没有其他线程可以在两者之间进行拦截。由于这是单处理器操作(现代计算机架构的魔力),它通常比使用同步块更快。但请记住,当需要原子更新多个变量时,AtomicReference 将无济于事。

我想添加一个完整的运行代码,可以在 Eclipse 中运行。它会清除许多混乱。这里有 22 个用户(MyTh 线程)试图预订 20 个座位。以下是代码片段,后面是完整代码。

22 个用户尝试预订 20 个座位的代码片段。

for (int i = 0; i < 20; i++) {// 20 seats
            seats.add(new AtomicReference<Integer>());
        }
        Thread[] ths = new Thread[22];// 22 users
        for (int i = 0; i < ths.length; i++) {
            ths[i] = new MyTh(seats, i);
            ths[i].start();
        }

以下是那些想要查看运行的完整代码的人的 github 链接,它小而简洁。 https://github.com/sankar4git/atomicReference/blob/master/Solution.java

于 2018-05-31T06:13:34.433 回答