日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

LongAccumulator和DoubleAccumulator类如何工作?

發布時間:2023/12/3 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 LongAccumulator和DoubleAccumulator类如何工作? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Java 8中的兩個新類值得關注: LongAccumulator和DoubleAccumulator 。 它們旨在安全地跨線程安全地累積 (稍后將進一步說明)。 一個測試值一千個單詞,所以它是這樣工作的:

class AccumulatorSpec extends Specification {public static final long A = 1public static final long B = 2public static final long C = 3public static final long D = -4public static final long INITIAL = 0Ldef 'should add few numbers'() {given:LongAccumulator accumulator = new LongAccumulator({ long x, long y -> x + y }, INITIAL)when:accumulator.accumulate(A)accumulator.accumulate(B)accumulator.accumulate(C)accumulator.accumulate(D)then:accumulator.get() == INITIAL + A + B + C + D}

因此,累加器采用二進制運算符并將初始值與每個累加值組合在一起。 這意味著((((0 + 1) + 2) + 3) + -4)等于2 。 別走開,還有更多。 累加器也可以采用其他運算符,如以下用例所示:

def 'should accumulate numbers using operator'() {given:LongAccumulator accumulator = new LongAccumulator(operator, initial)when:accumulator.accumulate(A)accumulator.accumulate(B)accumulator.accumulate(C)accumulator.accumulate(D)then:accumulator.get() == expectedwhere:operator | initial || expected{x, y -> x + y} | 0 || A + B + C + D{x, y -> x * y} | 1 || A * B * C * D{x, y -> Math.max(x, y)} | Integer.MIN_VALUE || max(A, B, C, D){x, y -> Math.min(x, y)} | Integer.MAX_VALUE || min(A, B, C, D) }

顯然,累加器在設計用于繁重的多線程環境下也能正常工作。 現在的問題是, LongAccumulator中允許進行其他哪些操作(這也適用于DoubleAccumulator ),為什么? JavaDoc這次不是很正式(粗體):

不能保證并且不能依賴于線程內或線程間的累加順序,因此此類僅適用于累加順序無關緊要的函數。 所提供的累加器功能應無副作用 ,因為當嘗試更新由于線程間爭用而失敗時,可以重新應用該累加器功能 。 應用該函數時,將當前值作為其第一個參數,并將給定的update作為第二個參數。

為了了解LongAccumulator工作原理,允許哪種類型的操作以及為何如此之快(因為與AtomicLong相比,它是如此之快),讓我們從后面開始get()方法:

transient volatile long base; transient volatile Cell[] cells;private final LongBinaryOperator function;public long get() {Cell[] as = cells; Cell a;long result = base;if (as != null) {for (int i = 0; i < as.length; ++i) {if ((a = as[i]) != null)result = function.applyAsLong(result, a.value);}}return result; }

可以將其重寫為不完全等效,但更易于閱讀:

public long get() {long result = base;for (Cell cell : cells)result = function.applyAsLong(result, cell.value);return result; }

甚至在功能上沒有內部狀態:

public long get() {return Arrays.stream(cells).map(s -> s.value).reduce(base, function::applyAsLong); }

我們清楚地看到有一些內部cells數組,最后我們必須遍歷該數組并將操作符函數順序應用于每個元素。 事實證明, LongAccumulator具有兩種用于累加值的機制:單個base計數器和在鎖線程爭用較高的情況下的值數組。 如果在沒有鎖爭用的情況下使用LongAccumulator ,則僅使用單個volatile base變量和CAS操作,就像在AtomicLong 。 但是,如果CAS失敗,則此類退回到一組值。 您不想看到實現,它有90行,有時會有8個嵌套層。 您需要知道的是,它使用簡單的算法始終將給定線程分配給同一單元(提高了緩存的局部性)。 從現在開始,該線程具有其自己的,幾乎是私有的計數器副本。 它與其他幾個線程共享此副本,但并非與所有其他線程共享-它們具有自己的單元。 因此,最終您得到的是必須匯總的半計算計數器數組。 這就是您在get()方法中看到的。

這又使我們想到一個問題, LongAccumulator中允許使用LongAccumulator op符( op )。 我們知道在低負載下相同的累積順序將導致:

((I op A) op B) //get()

這意味著所有值都聚集在基本變量中,并且不使用任何計數器數組。 但是,在高負載下, LongAccumulator會將工作分解為兩個鏟斗(單元),然后LongAccumulator鏟斗也進行蓄積:

(I op A) //cell 1 (I op B) //cell 2(I op A) op (I op B) //get()

或相反亦然:

(I op B) //cell 1 (I op A) //cell 2(I op B) op (I op A) //get()

顯然,所有對get()調用都應產生相同的結果,但這都取決于所提供的op符的屬性( + , * , max等)。

可交換的

我們無法控制單元的順序及其分配方式。 這就是((I op A) op (I op B))和((I op B) op (I op A))必須返回相同結果的原因。 更緊湊地說,我們正在尋找這樣的運算符op ,其中每個X和Y X op Y = Y op X 這意味著op必須是可交換的

中性元素(身份)

單元格使用標識(初始)值I進行邏輯初始化。 我們無法控制單元的數量和順序,因此身份值可??以按任意順序多次應用。 但這是一個實現細節,因此不應影響結果。 更確切地說,對于每個X和任何op :

X op I = I op X = X

這意味著對于運算符op每個參數X ,標識(初始)值I必須為中性值。

關聯性

假設我們有以下單元格:

I op A // cell 1 I op B // cell 2 I op C // cell 3 ((I op A) op (I op B)) op (I op C) //get()

但是下一次他們的布置有所不同

I op C // cell 1 I op B // cell 2 I op A // cell 2 ((I op C) op (I op B)) op (I op A) //get()

知道op是可交換的,而I是中性元素,我們可以證明(對于每個A , B和C ):

((I op A) op (I op B)) op (I op C) = ((I op C) op (I op B)) op (I op A) (A op B) op C = (C op B) op A (A op B) op C = A op (B op C)

這證明op必須具有關聯性 , LongAccumulator才能真正起作用。

包起來

LongAccumulator和DoubleAccumulator是JDK 8中新增的高度專業化的類。JavaDoc相當荒謬,但是我們嘗試證明操作員和初始值必須滿足的屬性才能使它們完成工作。 我們知道運算符必須是關聯的 , 可交換的并且具有中性元素。 如果JavaDoc明確聲明它必須是阿貝爾的類半體動物 ;-),那就更好了。 然而,出于實際目的,這些累加器僅用于加,乘,最小和最大值,因為它們是唯一發揮良好作用的有用運算符(帶有適當的中性元素)。 例如,減法和除法不是關聯和可交換的,因此不可能工作。 更糟的是,累加器只會表現得不確定。

翻譯自: https://www.javacodegeeks.com/2015/06/how-longaccumulator-and-doubleaccumulator-classes-work.html

總結

以上是生活随笔為你收集整理的LongAccumulator和DoubleAccumulator类如何工作?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。