Java 8 并發: 原子變量和 ConcurrentMap

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

AtomicInteger

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

在內部,原子類大量使用 CAS, 這是大多數現在 CPU 支持的原子操作指令, 這些指令通常情況下比鎖同步要快得多。如果需要同時改變一個變量, 使用原子類是極其優雅的。

現在選擇一個原子類 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 可以在線程安全的環境中增加變量, 而不要同步訪問變量。incrementAndGet() 方法是一個原子操作, 我們可以在多線程中安全的調用。

AtomicInteger 支持多種的原子操作, updateAndGet() 方法接受一個 lambda 表達式,以便對整數做任何的算術運算。

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 表達式, 我們是用這種方法來計算 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 類可以用來連續地向數字添加值。

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() 方法, 同時也是線程安全的。但其內部的結果不是一個單一的值, 這個類的內部維護了一組變量來減少多線程的爭用。實際結果可以通過調用 sum()sumThenReset() 來獲取。

當來自多線程的更新比讀取更頻繁時, 這個類往往優于其他的原子類。通常作為統計數據, 比如要統計 web 服務器的請求數量。 LongAdder 的缺點是會消耗更多的內存, 因為有一組變量保存在內存中。

LongAccumulator

LongAccumulatorLongAdder 的一個更通用的版本。它不是執行簡單的添加操作, 類 LongAccumulator 圍繞 LongBinaryOperator 類型的lambda表達式構建,如代碼示例中所示:

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
復制代碼

我們使用函數 2 * x + y 和初始值1創建一個 LongAccumulator。 每次調用 accumulate(i) , 當前結果和值i都作為參數傳遞給``lambda` 表達式。

LongAdder 一樣, LongAccumulator 在內部維護一組變量以減少對線程的爭用。

ConcurrentMap

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

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

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

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

map.forEach((key, value) -> System.out.printf("%s = %s\n", key, value));
復制代碼

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

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

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

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

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

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

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

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

ConcurrentHashMap

以上所有方法都是 ConcurrentMap 接口的一部分,因此可用于該接口的所有實現。 此外,最重要的實現 ConcurrentHashMap 已經進一步增強了一些新的方法來在 Map 上執行并發操作。

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

System.out.println(ForkJoinPool.getCommonPoolParallelism());  // 3
復制代碼

通過設置以下 JVM 參數可以減少或增加此值:

-Djava.util.concurrent.ForkJoinPool.common.parallelism=5
復制代碼

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

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

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

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

ForEach

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

map.forEach(1, (key, value) ->System.out.printf("key: %s; value: %s; thread: %s\n",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 類型的 lambda 表達式, 它能對 map 做搜索操作, 如果當前迭代不符合所需的搜索條件,則返回 null。 請記住,ConcurrentHashMap 是無序的。 搜索功能不應該取決于地圖的實際處理順序。 如果有多個匹配結果, 則結果可能是不確定的。

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
復制代碼

下面是對 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() 方法接受兩個類型為 BiFunctionlambda 表達式。 第一個函數將每個鍵值對轉換為任何類型的單個值。 第二個函數將所有這些轉換后的值組合成一個結果, 其中火忽略 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
復制代碼

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/278463.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/278463.shtml
英文地址,請注明出處:http://en.pswp.cn/news/278463.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

this表示當前對象簡單實例

直接上代碼 class Message { private Channel channel ; // 保存消息發送通道 private String title ; // 消息標題 private String content ; // 消息內容 // 4、調用此構造實例化&#xff0c;此時的channel 主類ch public Message(Channel channel,String title,String cont…

twitter推文不收錄_如何使用Twitter書簽保存推文供以后使用

twitter推文不收錄Khamosh PathakKhamosh PathakTwitter has a new Bookmarks feature that lets you privately save tweets for later. If you’ve been using the Like feature as a workaround for saving tweets, here’s why you should start bookmarking. Twitter具有一…

if的作用域問題 *輸出1~6的隨機數*

1 //測試if語句2 public class TestIf {3 public static void main(String[] args){4 double d Math.random();//0~1之間的小數5 int e (int)(d*5); //[0,4]6 //int f 1(int)(d*6); //[1,6] 擲色子7 System.out.println(e);8 …

為您的Blogger博客設計一個美麗的新主題

Would you like to give your Blogger blog a fresh coat of paint with a new theme? Here’s how you can use the new Template Designer to make your Blogger site stand out from the crowd and look great. 您想給Blogger博客一個新的主題嗎&#xff1f; 您可以通過以…

Lab 6-4

In this lab, we’ll analyze the malware found in the file Lab06-04.exe. Questions and Short Answers What is the difference between the calls made from the main method in Labs 6-3 and 6-4? A: The function at 0x401000 is the check Internet connection method…

步入三十歲前的總結:看似經歷很多得到很多,但,實際卻一無所得

本文算是一篇審視自己的文章吧&#xff0c;感覺跟我類似經歷的人應該很多&#xff0c;認同感應該也大一些。我是12年網絡專業很普通的一所大專院校畢業&#xff0c;到現在為止工作已經超過五年。這五年里&#xff0c;做過運維工程師&#xff0c;也在小車床工作間里做了一下技工…

vue---day03

1. Vue的生命周期 - 創建和銷毀的時候可以做一些我們自己的事情 - beforeCreated - created - beforeMount - mounted - beforeUpdate - updated - activated - deactivated - beforeDestroy - destroyed 1.1 知識點回顧 1.1.1 be…

U Sparkle 開發者計劃招募中!

向我們投稿吧 在此之前&#xff0c;我們有收到過幾篇民間高手的投稿&#xff0c;如&#xff1a; USequencer 初識&#xff08;作者&#xff1a;焱燚(七火)&#xff09; Unity游戲界面解決方案: PSD For UGUI&#xff08;作者&#xff1a;張俊欽&#xff09; UGUI 降低填充率技巧…

階乘和 大整數

///大整數階乘的和 #include<bits/stdc.h> using namespace std; int main() {int n;while(cin>>n){int a[2000] {1},b[2000] {0}; //存放結果的數組a。int c; //b用于存放每位存放的結果。int r0; //r用來表示進位的數。int h1,hb1; //h用來表示運算過程中 結果a…

如何添加引文標_如何在Google文檔中查找和添加引文

如何添加引文標When writing papers, you need to generate a detailed and accurate list of all the sources you’ve cited in your paper. With Google Docs, you can easily find and then add citations to all of your research papers. 撰寫論文時&#xff0c;您需要生…

mongo ttl索引

db.log_events.find() # 查找log_events里的所有數據db.log_events.createIndex( { "LogDT": 1 }, { expireAfterSeconds: 3600 } ) #設置log_events里的TTL過期索引清理時間為3600秒db.runComman…

Linux Centos下SQL Server 2017安裝和配置

Linux Centos下SQL Server 2017安裝和配置 原文:Linux Centos下SQL Server 2017安裝和配置我們知道在Linux下安裝服務有很多方式&#xff0c;最為簡單的也就是yum安裝&#xff0c;但是很多服務通過yum是無法安裝的&#xff0c;如果想使用yum安裝&#xff0c;需要指定yum安裝倉庫…

如何在Linux上使用端口敲門(以及為什么不應該這樣做)

Photographee.eu/ShutterstockPhotographee.eu/ShutterstockPort knocking is a way to secure a server by closing firewall ports—even those you know will be used. Those ports are opened on demand if—and only if—the connection request provides the secret knoc…

小到年貨大到產業,劉村長的扶貧模式有點厲害!

河北省阜平縣平石頭村的村民&#xff0c;今年春節再也不用頭疼買什么年貨&#xff0c;去哪買年貨的問題了&#xff0c;因為他們的“村長”劉強東&#xff0c;給每戶人家都送來了年貨大禮包&#xff01;大禮包里不僅有牛奶、果汁、毛衣、長褲、波司登羽絨服、枕頭、毛巾、炊大皇…

java - 匿名類

匿名內部類 概念&#xff1a;即內部類的簡化寫法 前提&#xff1a;存在一個類&#xff08;可以是具體類也可以是抽象類&#xff09;或接口 格式&#xff1a;new 類名或接口名{重寫的方法} 本質&#xff1a;創建的是繼承了類或實現了接口的子類匿名對 象。 匿名類總是final&…

leetcode 342. Power of Four

沒想出來不用循環的。記錄下。 如果是2的次方&#xff0c;必有num & (nums - 1) bool isPowerOfFour(int num) {if (num < 1) return false;if (num & (num - 1)) return false; // 排除不是2的倍數if (num & 0x55555555) return true; // 排除不是4的倍數&am…

克隆ubuntu硬盤_使用Ubuntu Live CD克隆硬盤

克隆ubuntu硬盤Whether you’re setting up multiple computers or doing a full backup, cloning hard drives is a common maintenance task. Don’t bother burning a new boot CD or paying for new software – you can do it easily with your Ubuntu Live CD. 無論是設置…

頁面緩存處理的幾種方法

html只要加在頭部就可以了. <HEAD> <META HTTP-EQUIV"Pragma" CONTENT"no-cache"> <META HTTP-EQUIV"Cache-Control" CONTENT"no-cache"> <META HTTP-EQUIV"Expires" CONTENT"0"> </H…

Nginx的Mainline version、Stable version、Legacy version的版本區別

Nginx官網提供了三個類型的版本Mainline version&#xff1a;Mainline 是 Nginx 目前主力在做的版本&#xff0c;可以說是開發版Stable version&#xff1a;最新穩定版&#xff0c;生產環境上建議使用的版本Legacy versions&#xff1a;遺留的老版本的穩定版 nginx下載地址&…

從Boxee的Amie Street訪問音樂

One of our favorite sites for discovering new music is Amie Street. Today we take a look at the Amie Street app for Boxee that allows you to access your favorite tunes from the Boxee interface. 我們最喜歡的發現新音樂的網站之一是Amie Street。 今天&#xff0…