簡體   English   中英

線程安全實現max

[英]Thread-safe implementation of max

我需要為Web服務器實現全局對象收集統計信息。 我有Statistics單例,它有方法addSample(long sample) ,隨后調用updateMax 這顯然是線程安全的。 我有這個方法來更新整個統計信息的最大值:

AtomicLong max;

private void updateMax(long sample) {
    while (true) {
        long curMax = max.get();
        if (curMax < sample) {
            boolean result = max.compareAndSet(curMax, sample);
            if (result) break;
        } else {
            break;
        }
    }
}

這個實現是否正確? 我正在使用java.util.concurrent,因為我相信它會比簡單synchronized更快。 是否有其他/更好的方法來實現這一點?

我認為這是正確的,但為了清晰起見,我可能會重寫一點,並且肯定會添加評論:

private void updateMax(long sample) {
    while (true) {
        long curMax = max.get();
        if (curMax >= sample) {
            // Current max is higher, so whatever other threads are
            // doing, our current sample can't change max.
            break;
        }

        // Try updating the max value, but only if it's equal to the
        // one we've just seen. We don't want to overwrite a potentially
        // higher value which has been set since our "get" call.
        boolean setSuccessful = max.compareAndSet(curMax, sample);

        if (setSuccessful) {
            // We managed to update the max value; no other threads
            // got in there first. We're definitely done.
            break;
        }

        // Another thread updated the max value between our get and
        // compareAndSet calls. Our sample can still be higher than the
        // new value though - go round and try again.
    }
}

編輯:通常我至少首先嘗試同步版本,只有當我發現它導致問題時才會使用這種無鎖代碼。

從Java 8開始, LongAccumulator已經推出。 建議為

當多個線程更新用於收集統計信息但不用於細粒度同步控制的目的的公共值時,此類通常優於AtomicLong。 在低更新爭用下,這兩個類具有相似的特征。 但在高爭用的情況下,這一類的預期吞吐量明顯更高,但代價是空間消耗更高。

您可以按如下方式使用它:

LongAccumulator maxId = new LongAccumulator(Long::max, 0); //replace 0 with desired initial value
maxId.accumulate(newValue); //from each thread

好像你沒有選擇答案,這是我的:

// while the update appears bigger than the atomic, try to update the atomic.
private void max(AtomicDouble atomicDouble, double update) {
    double expect = atomicDouble.get();
    while (update > expect) {
        atomicDouble.weakCompareAndSet(expect, update);
        expect = atomicDouble.get();
    }
}

它與接受的答案大致相同,但不使用我個人不喜歡的breakwhile(true)

編輯:剛剛在java 8中發現了DoubleAccumulator 。文檔甚至說這是針對像你這樣的摘要統計問題:

DoubleAccumulator max = new DoubleAccumulator(Double::max, Double.NEGATIVE_INFINITY);
parallelStream.forEach(max::accumulate);
max.get();

使用Java 8,您可以利用功能接口和簡單的lamda表達式來解決這個問題,只需一行而不是循環:

private void updateMax(long sample) {
    max.updateAndGet(curMax -> (sample > curMax) ? sample : curMax);
}

該解決方案使用updateAndGet(LongUnaryOperator)方法。 當前值包含在curMax並且使用條件運算符執行簡單測試,如果樣本值大於當前最大值,則使用樣本值替換當前最大值。

我相信你做的是正確的,但這是一個更簡單的版本,我認為是正確的。

private void updateMax(long sample){
      //this takes care of the case where between the comparison and update steps, another thread updates the max

      //For example:
      //if the max value is set to a higher max value than the current value in between the comparison and update step
      //sample will be the higher value from the other thread
      //this means that the sample will now be higher than the current highest (as we just set it to the value passed into this function)
      //on the next iteration of the while loop, we will update max to match the true max value
      //we will then fail the while loop check, and be done with trying to update.
      while(sample > max.get()){
          sample = max.getAndSet(sample);  
      }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM