1. 程式人生 > >JAVA 8 併發增強(1) 多執行緒修改某個計數器的方式

JAVA 8 併發增強(1) 多執行緒修改某個計數器的方式

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);