国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Java 8 并發(fā): 原子變量和 ConcurrentMap

yy13818512006 / 3592人閱讀

摘要:在有些情況下,原子操作可以在不使用關(guān)鍵字和鎖的情況下解決多線程安全問題。但其內(nèi)部的結(jié)果不是一個(gè)單一的值這個(gè)類的內(nèi)部維護(hù)了一組變量來減少多線程的爭(zhēng)用。當(dāng)來自多線程的更新比讀取更頻繁時(shí)這個(gè)類往往優(yōu)于其他的原子類。

原文地址: Java 8 Concurrency Tutorial: Atomic Variables and ConcurrentMap
AtomicInteger

java.concurrent.atomic 包下有很多原子操作的類。 在有些情況下,原子操作可以在不使用 synchronized 關(guān)鍵字和鎖的情況下解決多線程安全問題。

在內(nèi)部,原子類大量使用 CAS, 這是大多數(shù)現(xiàn)在 CPU 支持的原子操作指令, 這些指令通常情況下比鎖同步要快得多。如果需要同時(shí)改變一個(gè)變量, 使用原子類是極其優(yōu)雅的。

現(xiàn)在選擇一個(gè)原子類 AtomicInteger 作為例子

AtomicInteger atomicInt = new AtomicInteger(0);

ExecutorService executor = Executors.newFixedThreadPool(2);

IntStream.range(0, 1000)
    .forEach(i -> executor.submit(atomicInt::incrementAndGet));

stop(executor);

System.out.println(atomicInt.get());    // => 1000

使用 AtomicInteger 代替 Integer 可以在線程安全的環(huán)境中增加變量, 而不要同步訪問變量。incrementAndGet() 方法是一個(gè)原子操作, 我們可以在多線程中安全的調(diào)用。

AtomicInteger 支持多種的原子操作, updateAndGet() 方法接受一個(gè) lambda 表達(dá)式,以便對(duì)整數(shù)做任何的算術(shù)運(yùn)算。

AtomicInteger atomicInt = new AtomicInteger(0);

ExecutorService executor = Executors.newFixedThreadPool(2);

IntStream.range(0, 1000)
    .forEach(i -> {
        Runnable task = () ->
            atomicInt.updateAndGet(n -> n + 2);
        executor.submit(task);
    });

stop(executor);

System.out.println(atomicInt.get());    // => 2000

accumulateAndGet() 方法接受一個(gè) IntBinaryOperator 類型的另一種 lambda 表達(dá)式, 我們是用這種方法來計(jì)算 1 -- 999 的和:

AtomicInteger atomicInt = new AtomicInteger(0);

ExecutorService executor = Executors.newFixedThreadPool(2);

IntStream.range(0, 1000)
    .forEach(i -> {
        Runnable task = () ->
            atomicInt.accumulateAndGet(i, (n, m) -> n + m);
        executor.submit(task);
    });

stop(executor);

System.out.println(atomicInt.get());    // => 499500

還有一些其他的原子操作類: AtomicBoolean AtomicLong AtomicReference

LongAdder

作為 AtomicLong 的替代, LongAdder 類可以用來連續(xù)地向數(shù)字添加值。

ExecutorService executor = Executors.newFixedThreadPool(2);

IntStream.range(0, 1000)
    .forEach(i -> executor.submit(adder::increment));

stop(executor);

System.out.println(adder.sumThenReset());   // => 1000

LongAdder 類和其他的整數(shù)原子操作類一樣提供了 add()increment() 方法, 同時(shí)也是線程安全的。但其內(nèi)部的結(jié)果不是一個(gè)單一的值, 這個(gè)類的內(nèi)部維護(hù)了一組變量來減少多線程的爭(zhēng)用。實(shí)際結(jié)果可以通過調(diào)用 sum()sumThenReset() 來獲取。

當(dāng)來自多線程的更新比讀取更頻繁時(shí), 這個(gè)類往往優(yōu)于其他的原子類。通常作為統(tǒng)計(jì)數(shù)據(jù), 比如要統(tǒng)計(jì) web 服務(wù)器的請(qǐng)求數(shù)量。 LongAdder 的缺點(diǎn)是會(huì)消耗更多的內(nèi)存, 因?yàn)橛幸唤M變量保存在內(nèi)存中。

LongAccumulator

LongAccumulatorLongAdder 的一個(gè)更通用的版本。它不是執(zhí)行簡(jiǎn)單的添加操作, 類 LongAccumulator 圍繞 LongBinaryOperator 類型的lambda表達(dá)式構(gòu)建,如代碼示例中所示:

LongBinaryOperator op = (x, y) -> 2 * x + y;
LongAccumulator accumulator = new LongAccumulator(op, 1L);

ExecutorService executor = Executors.newFixedThreadPool(2);

IntStream.range(0, 10)
    .forEach(i -> executor.submit(() -> accumulator.accumulate(i)));

stop(executor);

System.out.println(accumulator.getThenReset());     // => 2539

我們使用函數(shù) 2 * x + y 和初始值1創(chuàng)建一個(gè) LongAccumulator。 每次調(diào)用 accumulate(i) , 當(dāng)前結(jié)果和值i都作為參數(shù)傳遞給`lambda 表達(dá)式。

LongAdder 一樣, LongAccumulator 在內(nèi)部維護(hù)一組變量以減少對(duì)線程的爭(zhēng)用。

ConcurrentMap

ConcurrentMap 接口擴(kuò)展了 Map 接口,并定義了最有用的并發(fā)集合類型之一。 Java 8 通過向此接口添加新方法引入了函數(shù)式編程。

在下面的代碼片段中, 來演示這些新的方法:

ConcurrentMap map = new ConcurrentHashMap<>();
map.put("foo", "bar");
map.put("han", "solo");
map.put("r2", "d2");
map.put("c3", "p0");

forEach() 接受一個(gè)類型為 BiConsumerlambda 表達(dá)式, 并將 mapkeyvalue 作為參數(shù)傳遞。

map.forEach((key, value) -> System.out.printf("%s = %s
", key, value));

putIfAbsent() 方法只有當(dāng)給定的 key 不存在時(shí)才將數(shù)據(jù)存入 map 中, 這個(gè)方法和 put 一樣是線程安全的, 當(dāng)多個(gè)線程訪問 map 時(shí)不要做同步操作。

String value = map.putIfAbsent("c3", "p1");
System.out.println(value);    // p0

getOrDefault() 方法返回給定 keyvalue, 當(dāng) key 不存在時(shí)返回給定的值。

String value = map.getOrDefault("hi", "there");
System.out.println(value);    // there

replaceAll() 方法接受一個(gè) BiFunction 類型的 lambda 表達(dá)式, 并將 keyvalue 作為參數(shù)傳遞,用來更新 value

map.replaceAll((key, value) -> "r2".equals(key) ? "d3" : value);
System.out.println(map.get("r2"));    // d3

compute() 方法和 replaceAll() 方法有些相同, 不同的是它多一個(gè)參數(shù), 用來更新指定 keyvalue

map.compute("foo", (key, value) -> value + value);
System.out.println(map.get("foo"));   // barbar
ConcurrentHashMap

以上所有方法都是 ConcurrentMap 接口的一部分,因此可用于該接口的所有實(shí)現(xiàn)。 此外,最重要的實(shí)現(xiàn) ConcurrentHashMap 已經(jīng)進(jìn)一步增強(qiáng)了一些新的方法來在 Map 上執(zhí)行并發(fā)操作。

就像并行流一樣,這些方法在 Java 8 中通過 ForkJoinPool.commonPool()提供特殊的 ForkJoinPool 。該池使用預(yù)設(shè)的并行性, 這取決于可用內(nèi)核的數(shù)量。 我的機(jī)器上有四個(gè)CPU內(nèi)核可以實(shí)現(xiàn)三種并行性:

System.out.println(ForkJoinPool.getCommonPoolParallelism());  // 3

通過設(shè)置以下 JVM 參數(shù)可以減少或增加此值:

-Djava.util.concurrent.ForkJoinPool.common.parallelism=5

我們使用相同的示例來演示, 不過下面使用 ConcurrentHashMap 類型, 這樣可以調(diào)用更多的方法。

ConcurrentHashMap map = new ConcurrentHashMap<>();
map.put("foo", "bar");
map.put("han", "solo");
map.put("r2", "d2");
map.put("c3", "p0");

Java 8 引入了三種并行操作:forEach, searchreduce。 每個(gè)操作都有四種形式, 分別用 key, value, entrieskey-value 來作為參數(shù)。

所有這些方法的第一個(gè)參數(shù)都是 parallelismThreshold 閥值。 該閾值表示操作并行執(zhí)行時(shí)的最小收集大小。 例如, 如果傳遞的閾值為500,并且 map 的實(shí)際大小為499, 則操作將在單個(gè)線程上按順序執(zhí)行。 在下面的例子中,我們使用一個(gè)閾值來強(qiáng)制并行操作。

ForEach

方法 forEach() 能夠并行地迭代 map 的鍵值對(duì)。 BiConsumer 類型的 lambda 表達(dá)式接受當(dāng)前迭代的 keyvalue。 為了可視化并行執(zhí)行,我們將當(dāng)前線程名稱打印到控制臺(tái)。 請(qǐng)記住,在我的情況下,底層的 ForkJoinPool 最多使用三個(gè)線程。

map.forEach(1, (key, value) ->
    System.out.printf("key: %s; value: %s; thread: %s
",
        key, value, Thread.currentThread().getName()));

// key: r2; value: d2; thread: main
// key: foo; value: bar; thread: ForkJoinPool.commonPool-worker-1
// key: han; value: solo; thread: ForkJoinPool.commonPool-worker-2
// key: c3; value: p0; thread: main
Search

search() 方法接受一個(gè) BiFunction 類型的 lambda 表達(dá)式, 它能對(duì) map 做搜索操作, 如果當(dāng)前迭代不符合所需的搜索條件,則返回 null。 請(qǐng)記住,ConcurrentHashMap 是無(wú)序的。 搜索功能不應(yīng)該取決于地圖的實(shí)際處理順序。 如果有多個(gè)匹配結(jié)果, 則結(jié)果可能是不確定的。

String result = map.search(1, (key, value) -> {
    System.out.println(Thread.currentThread().getName());
    if ("foo".equals(key)) {
        return value;
    }
    return null;
});
System.out.println("Result: " + result);

// ForkJoinPool.commonPool-worker-2
// main
// ForkJoinPool.commonPool-worker-3
// Result: bar

下面是對(duì) value 的搜索

String result = map.searchValues(1, value -> {
    System.out.println(Thread.currentThread().getName());
    if (value.length() > 3) {
        return value;
    }
    return null;
});

System.out.println("Result: " + result);

// ForkJoinPool.commonPool-worker-2
// main
// main
// ForkJoinPool.commonPool-worker-1
// Result: solo
Reduce

reduce() 方法接受兩個(gè)類型為 BiFunctionlambda 表達(dá)式。 第一個(gè)函數(shù)將每個(gè)鍵值對(duì)轉(zhuǎn)換為任何類型的單個(gè)值。 第二個(gè)函數(shù)將所有這些轉(zhuǎn)換后的值組合成一個(gè)結(jié)果, 其中火忽略 null 值。

String result = map.reduce(1,
    (key, value) -> {
        System.out.println("Transform: " + Thread.currentThread().getName());
        return key + "=" + value;
    },
    (s1, s2) -> {
        System.out.println("Reduce: " + Thread.currentThread().getName());
        return s1 + ", " + s2;
    });

System.out.println("Result: " + result);

// Transform: ForkJoinPool.commonPool-worker-2
// Transform: main
// Transform: ForkJoinPool.commonPool-worker-3
// Reduce: ForkJoinPool.commonPool-worker-3
// Transform: main
// Reduce: main
// Reduce: main
// Result: r2=d2, c3=p0, han=solo, foo=bar

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/68434.html

相關(guān)文章

  • Java 8 并發(fā)教程:原子變量 ConcurrentMa

    摘要:并發(fā)教程原子變量和原文譯者飛龍協(xié)議歡迎閱讀我的多線程編程系列教程的第三部分。如果你能夠在多線程中同時(shí)且安全地執(zhí)行某個(gè)操作,而不需要關(guān)鍵字或上一章中的鎖,那么這個(gè)操作就是原子的。當(dāng)多線程的更新比讀取更頻繁時(shí),這個(gè)類通常比原子數(shù)值類性能更好。 Java 8 并發(fā)教程:原子變量和 ConcurrentMap 原文:Java 8 Concurrency Tutorial: Synchroni...

    bitkylin 評(píng)論0 收藏0
  • Java? 教程(高級(jí)并發(fā)對(duì)象)

    高級(jí)并發(fā)對(duì)象 到目前為止,本課程重點(diǎn)關(guān)注從一開始就是Java平臺(tái)一部分的低級(jí)別API,這些API適用于非常基礎(chǔ)的任務(wù),但更高級(jí)的任務(wù)需要更高級(jí)別的構(gòu)建塊,對(duì)于充分利用當(dāng)今多處理器和多核系統(tǒng)的大規(guī)模并發(fā)應(yīng)用程序尤其如此。 在本節(jié)中,我們將介紹Java平臺(tái)5.0版中引入的一些高級(jí)并發(fā)功能,大多數(shù)這些功能都在新的java.util.concurrent包中實(shí)現(xiàn),Java集合框架中還有新的并發(fā)數(shù)據(jù)結(jié)構(gòu)。 ...

    xiaotianyi 評(píng)論0 收藏0
  • Java 8 并發(fā)教程:同步

    摘要:在接下來的分鐘,你將會(huì)學(xué)會(huì)如何通過同步關(guān)鍵字,鎖和信號(hào)量來同步訪問共享可變變量。所以在使用樂觀鎖時(shí),你需要每次在訪問任何共享可變變量之后都要檢查鎖,來確保讀鎖仍然有效。 原文:Java 8 Concurrency Tutorial: Synchronization and Locks譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 歡迎閱讀我的Java8并發(fā)教程的第二部分。這份指南將...

    wyk1184 評(píng)論0 收藏0
  • Java 并發(fā)方案全面學(xué)習(xí)總結(jié)

    摘要:進(jìn)程線程與協(xié)程它們都是并行機(jī)制的解決方案。選擇是任意性的,并在對(duì)實(shí)現(xiàn)做出決定時(shí)發(fā)生。線程池的大小一旦達(dá)到最大值就會(huì)保持不變,如果某個(gè)線程因?yàn)閳?zhí)行異常而結(jié)束,那么線程池會(huì)補(bǔ)充一個(gè)新線程。此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求。 并發(fā)與并行的概念 并發(fā)(Concurrency): 問題域中的概念—— 程序需要被設(shè)計(jì)成能夠處理多個(gè)同時(shí)(或者幾乎同時(shí))發(fā)生的事件 并行(Parallel...

    mengera88 評(píng)論0 收藏0
  • Java 8 并發(fā)教程:線程執(zhí)行器

    摘要:在這個(gè)示例中我們使用了一個(gè)單線程線程池的。在延遲消逝后,任務(wù)將會(huì)并發(fā)執(zhí)行。這是并發(fā)系列教程的第一部分。第一部分線程和執(zhí)行器第二部分同步和鎖第三部分原子操作和 Java 8 并發(fā)教程:線程和執(zhí)行器 原文:Java 8 Concurrency Tutorial: Threads and Executors 譯者:BlankKelly 來源:Java8并發(fā)教程:Threads和Execut...

    jsdt 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<