有条件地检查 Atomic Integer 线程是否安全?

Conditional checking on Atomic Integer thread safe?

示例代码

@ApplicationScoped
public class AtomicIntegerSample {

private final AtomicInteger successFullSyncCount = new AtomicInteger(0);
private final AtomicLong overallSyncTimeTaken  = new AtomicLong(0);

public void incrementSuccessFullSyncCount() {
    this.successFullSyncCount.incrementAndGet();
}


public void addOverallSyncTimeTaken(final Long syncTimeTaken) {
    overallSyncTimeTaken.addAndGet(syncTimeTaken);
}
/**
 * Can be called by multiple threads
 * @return
 */
public long getAverageSuccessfulSyncTimeTaken() {
    if (successFullSyncCount.get() == 0) {
        return 0;
    } else {
        return overallSyncTimeTaken.get() / successFullSyncCount.get();
    }
}

在方法getAverageSuccessfulSyncTimeTaken()中有条件检查以避免算术异常。

classApplicationScoped,此方法可被多线程并发调用。

方法线程安全吗?。如果我把代码替换成下面这样,线程安全吗?

 /**
 * Can be called by multiple threads
 * @return
 */
public long getAverageSuccessfulSyncTimeTaken() {
       return overallSyncTimeTaken.get() / successFullSyncCount.get();

}

然后会抛出算术异常。如何以最佳方式同步此代码?。我的意思是只有 successFullSyncCount.get() == 0

它不会抛出任何异常,但它可能 return 不正确的结果:

  • 时间和计数分别递增。它们不会在单个原子操作中同时递增
  • 时间和计数是分开读取的,而不是在单个原子操作中同时读取。

因此,您可以很好地在一个线程中读取 100 的时间,然后让另一个线程将计数递增几次,然后读取计数并执行除法。因此,这可能导致每次同步的平均时间低于实际情况。它可能是一个足够好的近似值,也可能不是。

以下是不使用原子对象的版本。

如果您希望时间和计数一起更新,同步块可能更合适。这将防止您的平均方法被多次关闭。诚然,同步块比原生原子实现慢,但我不确定优化是否为时过早。

@ApplicationScoped
public class AtomicIntegerSample {

private int successFullSyncCount = 0;
private long overallSyncTimeTaken  = 0;


synchronized public void addSyncTimeTakenAndIncrementSyncCount(final Long syncTimeTaken) {
    overallSyncTimeTaken+=syncTimeTaken;
    successFullSyncCount++;
}
/**
 * Can be called by multiple threads
 * @return
 */
public long getAverageSuccessfulSyncTimeTaken() {
    if (successFullSyncCount.get() == 0) {
        return 0;
    } else {
        return overallSyncTimeTaken.get() / (float) successFullSyncCount.get();
    }
}