天天看點

Java SE 8 并發增強

1.原子值

java5開始,提供了一些原子操作的類,如AtomicInteger、AtomicLong等

這些類提供了諸如incrementAndGet這樣的原子操作方法。

單數如果想進行複雜操作,則需要使用compareAndSet進行循環處理

在java8中提供了updateAndGet和accumulateAndGet方法

atomicLong,updateAndGet(x -> Max.max(x, observed));

atomicLong.accumulateAndGet(observed, Math::max);

同時也提供了傳回原始值的對應方法:getAndUpdate、getAndAccumulate

------------------------------------------------------

當大量線程通路同一個原始值時,由于樂觀鎖重試次數太多會導緻性能下降

Java8為此提供了LongAdder和LongAccumulator解決該問題

其思想為将初始值變為多個中立元素,計算時不同線程可以對不同元素進行操作,最後再将操作結果合并。

例如:

此時在LongAccumulator 中包含多個中立元素a1,a2...aN.該例子下中立元素初始值都為零。當調用accumulate方法累加value時,這些變量的其中之一被更新為ai = ai op v。在這個實力中ai = ai + v;

而最後調用get方法的時候,結果為a1 op a2 op ... aN. 在上述例子中為a1+a2+...aN

java8中還添加了StampedLock類實作樂觀讀

調用tryOptimisticRead方法時會擷取一個印戳,當讀取值并檢測印戳有效,則可以使用這個值,否則會獲得一個阻塞所有寫鎖的讀鎖

例:

2.ConcurrentHashMap改進

1. 更新值

concurrentHashMap在更新數值的時候雖然是線程安全的,但是在計算更新值的時候由于不能保證線程安全,更新的值可能是錯誤的。

一種補救措施是使用replace

此外還可以使用利用原子對象,例如CuncurrentHashMap<String, LongAdder>

如果需要複雜計算,compute方法可以通過一個函數來計算新的值

xxxIfPresent和xxxIfAbsent方法分别表示已經存在值或者尚未存在值的情況下才進行操作

merge方法可以在key第一次加入時做一些特殊操作,第二個參數表示鍵尚未存在時的初始值

--------------------------------------------------------------------------

2. 批量資料操作

・search會對每個鍵值對領用一個函數,直到函數傳回非null,search會終止并傳回函數結果

・reduce會通過提供的累計函數,将所有鍵值對組合起來

・foreach會對所有鍵值對應用一個函數

每個操作都有4個版本:

• operation Keys : 對鍵操作

• operation Values : 對值操作

• operation: 對鍵和值操作

• operation Entries : 對  Map.Entry 對象操作.

以search為例,有以下幾個方法:

U searchKeys(long threshold, BiFunction<? super K, ? extends U> f)

U searchValues(long threshold, BiFunction<? super V, ? extends U> f)

U search(long threshold, BiFunction<? super K, ? super V,? extends U> f)

U searchEntries(long threshold, BiFunction<Map.Entry<K, V>, ? extends U> f)

threshold為并行閥值,如果包含的元素數量超過閥值,操作會以并行方式執行,如果希望永遠以單線程執行,請使用Long.MAX_VALUE

foreach和reduce方法除了上述形式外,還有另一種形式,可以提供一個轉換器函數,首先會應用轉換器函數,然後再将結果傳遞給消費者函數

對于int、long和double,reduce操作提供了專門的方法。以toXXX開頭,需要将輸入值轉換為原始類型值,并指定一個預設值和累加器函數

-----------------------------------------------------------------------------

3. Set視圖

java8沒有提供concurrenHashSet類,但是可以通過concurrentHashMap類通過虛假值獲得一個映射

靜态方法newKeySet會傳回一個Set<K>對象,它實際上是對ConcurrentHashMap<K, Boolean>對象的封裝。

如果你已經有一個映射,keySet方法會傳回所有鍵的Set,但是你不能向這個set中添加元素,因為無法向map添加相應的值

于是,一個接收預設值的keySet方法可以解決上述問題,通過這個預設值向set中添加元素

key=java, value = 1L

3.并行數組操作

Arrays提供許多并行化操作

parallelSort可以進行并行排序,并且可以指定範圍

 1 values.parallelSort(values.length / 2, values.length); // 對上半部排序  

parallelSetAll方法會根據提供的計算函數對參數values的每一個值進行計算并更新

parallelPrefix将數組中每個元素替換為指定關聯操作字首的積累

假設array [1, 2, 3, 4, ...],執行完Arrays.parallelPrefix(values, (x, y) -> x * y)之後,array的結果為

[1, 1 × 2, 1 × 2 × 3, 1 × 2 × 3 × 4, ...]

4.可完成的Future

在過去,Future擷取結果的方法為get,并且調用後會一直阻塞等待get傳回結果

CompletableFuture<T>提供了“當結果可用時,再按照提供的方式處理”的功能

thenApply方法不會被阻塞,它會傳回另一個Future對象,當第一個Future對象完成時,它的結果會發給getLinks方法

Future流水線類似Steam流水線,經過一個或多個轉換過程,最後由一個終止操作結束。

如下代碼可以啟動一個流水線

另外還有一個runAsync方法,接收Runnable參數,傳回CompletableFuture<void>

接下來可以調用thenApply或者thenApplyAsync方法,在同一個線程或者另一個線程中運作另一個操作。

最終這些步驟執行完畢,需要将結果儲存在某個地方,需要一個終止操作,例如:

thenAccept方法接收一個Consumer接口(傳回類型為void),理想情況下不需要調用Future的get方法

以下是一些常用方法:

Java SE 8 并發增強

thenCompose方法做的事就是,假設同時有兩個調用鍊,T->CompletableFuture<U>和U->CompletableFuture<V>在連續調用的情況下,合并為T->CompletableFuture<V>

類似的常用方法如下:

Java SE 8 并發增強