JAVA 8 併發增強(1) 多執行緒修改某個計數器的方式
阿新 • • 發佈:2018-12-12
Q:如何正確的併發修改一個AtomicLong的值?
/* 不同執行緒檢測最大值 */ AtomicLong largest = new AtomicLong(); long obsvValue = 0; /* 錯誤的方式,此更新不是原子性的 */ largest.set(Math.max(largest.get(), obsvValue)); /* 正確的方式,這種方式比鎖快 */ long oldValue; long newValue; do { oldValue = largest.get(); newValue = Math.max(obsvValue, oldValue); } while (!largest.compareAndSet(oldValue, newValue)); /* J8的方式 */ largest.updateAndGet(x -> Math.max(x, obsvValue)); /* 或者 */ largest.accumulateAndGet(obsvValue, Math::max); /* * 當有大量執行緒訪問同一個原子值的時,由於樂觀鎖更需要太多次重試,因此會導致效能嚴重下降 * J8提供了LongAdder和LongAccumulator來解決該問題。LongAdder由多個變數組成,這些變數累加的值 * 就是當前值。多個執行緒可以更新不同的被加數 * ,當執行緒數量增加時會自動增加新的被加數。由於通常情況下都是直到所有工作完成後才需要總和值,所以這種方法效率很高 * 如果你的環境存在高度競爭,那就應當用LongAdder來代替AtomicLong二者之間的方法命名稍有不同 * 。Increment方法用來將計數器自增1,add方法用來加上某個數值,sum方法用來獲取總和值 * tips:increment方法不會返回原始值。使用它只會抹殺掉總和值,拆分為多個被加數所帶來的效能提升 */ final LongAdder adder = new LongAdder(); adder.increment(); adder.increment(); adder.increment(); long total = adder.sum(); /* * LongAccumulator將這個思想帶到了任意的累加操作中。在建構函式中,你需要提供操作型別及其中立元素 * 要與新值相加,你需要呼叫accumulate方法。飯後再呼叫get方法獲取當前值。以下程式碼與LongAdder是一樣的 */ LongAccumulator adder2 = new LongAccumulator(Long::sum, 0); /*在某些執行緒中**/ adder2.accumulate(52);