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

資訊專欄INFORMATION COLUMN

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

bitkylin / 2351人閱讀

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

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

原文:Java 8 Concurrency Tutorial: Synchronization and Locks

譯者:飛龍

協(xié)議:CC BY-NC-SA 4.0

歡迎閱讀我的Java8多線程編程系列教程的第三部分。這個(gè)教程包含并發(fā)API的兩個(gè)重要部分:原子變量和ConcurrentMap。由于最近發(fā)布的Java8中的lambda表達(dá)式和函數(shù)式編程,二者都有了極大的改進(jìn)。所有這些新特性會(huì)以一些簡(jiǎn)單易懂的代碼示例來(lái)描述。希望你能喜歡。

第一部分:線程和執(zhí)行器

第二部分:同步和鎖

第三部分:原子變量和 ConcurrentMap

出于簡(jiǎn)單的因素,這個(gè)教程的代碼示例使用了定義在這里的兩個(gè)輔助函數(shù)sleep(seconds)stop(executor)

AtomicInteger

java.concurrent.atomic包包含了許多實(shí)用的類,用于執(zhí)行原子操作。如果你能夠在多線程中同時(shí)且安全地執(zhí)行某個(gè)操作,而不需要synchronized關(guān)鍵字或上一章中的鎖,那么這個(gè)操作就是原子的。

本質(zhì)上,原子操作嚴(yán)重依賴于比較與交換(CAS),它是由多數(shù)現(xiàn)代CPU直接支持的原子指令。這些指令通常比同步塊要快。所以在只需要并發(fā)修改單個(gè)可變變量的情況下,我建議你優(yōu)先使用原子類,而不是上一章展示的鎖。

譯者注:對(duì)于其它語(yǔ)言,一些語(yǔ)言的原子操作用鎖實(shí)現(xiàn),而不是原子指令。

現(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

通過(guò)使用AtomicInteger代替Integer,我們就能線程安全地并發(fā)增加數(shù)值,而不需要同步訪問(wèn)變量。incrementAndGet()方法是原子操作,所以我們可以在多個(gè)線程中安全調(diào)用它。

AtomicInteger支持多種原子操作。updateAndGet()接受lambda表達(dá)式,以便在整數(shù)上執(zhí)行任意操作:

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()方法接受另一種類型IntBinaryOperator的lambda表達(dá)式。我們?cè)谙聜€(gè)例子中,使用這個(gè)方法并發(fā)計(jì)算0~1000所有值的和:

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

其它實(shí)用的原子類有AtomicBooleanAtomicLongAtomicReference

LongAdder

LongAdderAtomicLong的替代,用于向某個(gè)數(shù)值連續(xù)添加值。

ExecutorService executor = Executors.newFixedThreadPool(2);

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

stop(executor);

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

LongAdder提供了add()increment()方法,就像原子數(shù)值類一樣,同樣是線程安全的。但是這個(gè)類在內(nèi)部維護(hù)一系列變量來(lái)減少線程之間的爭(zhēng)用,而不是求和計(jì)算單一結(jié)果。實(shí)際的結(jié)果可以通過(guò)調(diào)用sum()sumThenReset()來(lái)獲取。

當(dāng)多線程的更新比讀取更頻繁時(shí),這個(gè)類通常比原子數(shù)值類性能更好。這種情況在抓取統(tǒng)計(jì)數(shù)據(jù)時(shí)經(jīng)常出現(xiàn),例如,你希望統(tǒng)計(jì)Web服務(wù)器上請(qǐng)求的數(shù)量。LongAdder缺點(diǎn)是較高的內(nèi)存開銷,因?yàn)樗趦?nèi)存中儲(chǔ)存了一系列變量。

LongAccumulator

LongAccumulatorLongAdder的更通用的版本。LongAccumulator以類型為LongBinaryOperatorlambda表達(dá)式構(gòu)建,而不是僅僅執(zhí)行加法操作,像這段代碼展示的那樣:

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創(chuàng)建了LongAccumulator,初始值為1。每次調(diào)用accumulate(i)的時(shí)候,當(dāng)前結(jié)果和值i都會(huì)作為參數(shù)傳入lambda表達(dá)式。

LongAccumulator就像LongAdder那樣,在內(nèi)部維護(hù)一系列變量來(lái)減少線程之間的爭(zhēng)用。

ConcurrentMap

ConcurrentMap接口繼承自Map接口,并定義了最實(shí)用的并發(fā)集合類型之一。Java8通過(guò)將新的方法添加到這個(gè)接口,引入了函數(shù)式編程。

在下面的代碼中,我們使用這個(gè)映射示例來(lái)展示那些新的方法:

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

forEach()方法接受類型為BiConsumer的lambda表達(dá)式,以映射的鍵和值作為參數(shù)傳遞。它可以作為for-each循環(huán)的替代,來(lái)遍歷并發(fā)映射中的元素。迭代在當(dāng)前線程上串行執(zhí)行。

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

新方法putIfAbsent()只在提供的鍵不存在時(shí),將新的值添加到映射中。至少在ConcurrentHashMap的實(shí)現(xiàn)中,這一方法像put()一樣是線程安全的,所以你在不同線程中并發(fā)訪問(wèn)映射時(shí),不需要任何同步機(jī)制。

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

getOrDefault()方法返回指定鍵的值。在傳入的鍵不存在時(shí),會(huì)返回默認(rèn)值:

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

replaceAll()接受類型為BiFunction的lambda表達(dá)式。BiFunction接受兩個(gè)參數(shù)并返回一個(gè)值。函數(shù)在這里以每個(gè)元素的鍵和值調(diào)用,并返回要映射到當(dāng)前鍵的新值。

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

compute()允許我們轉(zhuǎn)換單個(gè)元素,而不是替換映射中的所有值。這個(gè)方法接受需要處理的鍵,和用于指定值的轉(zhuǎn)換的BiFunction

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

除了compute()之外還有兩個(gè)變體:computeIfAbsent()computeIfPresent()。這些方法的函數(shù)式參數(shù)只在鍵不存在或存在時(shí)被調(diào)用。

最后,merge()方法可以用于以映射中的現(xiàn)有值來(lái)統(tǒng)一新的值。這個(gè)方法接受鍵、需要并入現(xiàn)有元素的新值,以及指定兩個(gè)值的合并行為的BiFunction

map.merge("foo", "boo", (oldVal, newVal) -> newVal + " was " + oldVal);
System.out.println(map.get("foo"));   // boo was foo
ConcurrentHashMap

所有這些方法都是ConcurrentMap接口的一部分,因此可在所有該接口的實(shí)現(xiàn)上調(diào)用。此外,最重要的實(shí)現(xiàn)ConcurrentHashMap使用了一些新的方法來(lái)改進(jìn),便于在映射上執(zhí)行并行操作。

就像并行流那樣,這些方法使用特定的ForkJoinPool,由Java8中的ForkJoinPool.commonPool()提供。該池使用了取決于可用核心數(shù)量的預(yù)置并行機(jī)制。我的電腦有四個(gè)核心可用,這會(huì)使并行性的結(jié)果為3:

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

這個(gè)值可以通過(guò)設(shè)置下列JVM參數(shù)來(lái)增減:

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

我們使用相同的映射示例來(lái)展示,但是這次我們使用具體的ConcurrentHashMap實(shí)現(xiàn)而不是ConcurrentMap接口,所以我們可以訪問(wèn)這個(gè)類的所有公共方法:

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

Java8引入了三種類型的并行操作:forEachsearchreduce。這些操作中每個(gè)都以四種形式提供,接受以鍵、值、元素或鍵值對(duì)為參數(shù)的函數(shù)。

所有這些方法的第一個(gè)參數(shù)是通用的parallelismThreshold。這一閾值表示操作并行執(zhí)行時(shí)的最小集合大小。例如,如果你傳入閾值500,而映射的實(shí)際大小是499,那么操作就會(huì)在單線程上串行執(zhí)行。在下一個(gè)例子中,我們使用閾值1,始終強(qiáng)制并行執(zhí)行來(lái)展示。

forEach

forEach()方法可以并行迭代映射中的鍵值對(duì)。BiConsumer以當(dāng)前迭代元素的鍵和值調(diào)用。為了將并行執(zhí)行可視化,我們向控制臺(tái)打印了當(dā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()方法接受BiFunction并為當(dāng)前的鍵值對(duì)返回一個(gè)非空的搜索結(jié)果,或者在當(dāng)前迭代不匹配任何搜索條件時(shí)返回null。只要返回了非空的結(jié)果,就不會(huì)往下搜索了。要記住ConcurrentHashMap是無(wú)序的。搜索函數(shù)應(yīng)該不依賴于映射實(shí)際的處理順序。如果映射的多個(gè)元素都滿足指定搜索函數(shù),結(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

下面是另一個(gè)例子,僅僅搜索映射中的值:

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()方法已經(jīng)在Java 8 的數(shù)據(jù)流之中用過(guò)了,它接受兩個(gè)BiFunction類型的lambda表達(dá)式。第一個(gè)函數(shù)將每個(gè)鍵值對(duì)轉(zhuǎn)換為任意類型的單一值。第二個(gè)函數(shù)將所有這些轉(zhuǎn)換后的值組合為單一結(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

我希望你能喜歡我的Java8并發(fā)系列教程的第三部分。這個(gè)教程的代碼示例托管在Github上,還有許多其它的Java8代碼片段。歡迎fork我的倉(cāng)庫(kù)并自己嘗試。

如果你想要支持我的工作,請(qǐng)向你的朋友分享這篇教程。你也可以在Twiiter上關(guān)注我,因?yàn)槲視?huì)不斷推送一些Java或編程相關(guān)的東西。

第一部分:線程和執(zhí)行器

第二部分:同步和鎖

第三部分:原子變量和 ConcurrentMap

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

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

相關(guān)文章

  • Java 8 并發(fā)教程:同步

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

    wyk1184 評(píng)論0 收藏0
  • Java? 教程(同步)

    同步 線程主要通過(guò)共享對(duì)字段和引用對(duì)象的引用字段的訪問(wèn)來(lái)進(jìn)行通信,這種通信形式非常有效,但可能產(chǎn)生兩種錯(cuò)誤:線程干擾和內(nèi)存一致性錯(cuò)誤,防止這些錯(cuò)誤所需的工具是同步。 但是,同步可能會(huì)引入線程競(jìng)爭(zhēng),當(dāng)兩個(gè)或多個(gè)線程同時(shí)嘗試訪問(wèn)同一資源并導(dǎo)致Java運(yùn)行時(shí)更慢地執(zhí)行一個(gè)或多個(gè)線程,甚至?xí)和K鼈儓?zhí)行,饑餓和活鎖是線程競(jìng)爭(zhēng)的形式。 本節(jié)包括以下主題: 線程干擾描述了當(dāng)多個(gè)線程訪問(wèn)共享數(shù)據(jù)時(shí)如何引入錯(cuò)誤。...

    Edison 評(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 多線程并發(fā)基礎(chǔ)面試問(wèn)答

    摘要:多線程和并發(fā)問(wèn)題是技術(shù)面試中面試官比較喜歡問(wèn)的問(wèn)題之一。線程可以被稱為輕量級(jí)進(jìn)程。一個(gè)守護(hù)線程是在后臺(tái)執(zhí)行并且不會(huì)阻止終止的線程。其他的線程狀態(tài)還有,和。上下文切換是多任務(wù)操作系統(tǒng)和多線程環(huán)境的基本特征。 多線程和并發(fā)問(wèn)題是 Java 技術(shù)面試中面試官比較喜歡問(wèn)的問(wèn)題之一。在這里,從面試的角度列出了大部分重要的問(wèn)題,但是你仍然應(yīng)該牢固的掌握J(rèn)ava多線程基礎(chǔ)知識(shí)來(lái)對(duì)應(yīng)日后碰到的問(wèn)題。(...

    dreamans 評(píng)論0 收藏0
  • Java核心技術(shù)教程整理,長(zhǎng)期更新

    以下是Java技術(shù)棧微信公眾號(hào)發(fā)布的關(guān)于 Java 的技術(shù)干貨,從以下幾個(gè)方面匯總。 Java 基礎(chǔ)篇 Java 集合篇 Java 多線程篇 Java JVM篇 Java 進(jìn)階篇 Java 新特性篇 Java 工具篇 Java 書籍篇 Java基礎(chǔ)篇 8張圖帶你輕松溫習(xí) Java 知識(shí) Java父類強(qiáng)制轉(zhuǎn)換子類原則 一張圖搞清楚 Java 異常機(jī)制 通用唯一標(biāo)識(shí)碼UUID的介紹及使用 字符串...

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

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

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<