java 并发计数器_Java 8 LongAdders:管理并发计数器的正确方法
java 并發(fā)計(jì)數(shù)器
我只是買(mǎi)了新玩具,而Java 8有很多 。 這次我想談?wù)勎业淖類(lèi)?ài)之一-并發(fā)加法器。 這是一組新的類(lèi),用于管理由多個(gè)線程編寫(xiě)和讀取的計(jì)數(shù)器。 新的API有望顯著提高性能,同時(shí)仍使事情簡(jiǎn)單明了。
自從多核體系結(jié)構(gòu)問(wèn)世以來(lái)人們一直在管理并發(fā)計(jì)數(shù)器,讓我們看一看迄今為止Java提供的一些選項(xiàng)以及與新API相比它們的性能。
臟計(jì)數(shù)器 –這種方法意味著您正在多個(gè)線程之間的常規(guī)對(duì)象或靜態(tài)字段中進(jìn)行寫(xiě)入/讀取操作。 不幸的是,這有兩個(gè)原因。 首先是在Java中,A + = B操作不是原子操作。 如果打開(kāi)輸出字節(jié)碼,將至少看到四條指令-一個(gè)用于將堆中的字段值加載到線程堆棧中,第二個(gè)用于加載增量,第三個(gè)用于添加它們,第四個(gè)用于設(shè)置結(jié)果進(jìn)入領(lǐng)域。
如果在同一個(gè)內(nèi)存位置同時(shí)有多個(gè)線程同時(shí)執(zhí)行此操作,則極有可能錯(cuò)過(guò)寫(xiě)操作,因?yàn)橐粋€(gè)線程可以覆蓋另一個(gè)線程的值(又稱(chēng)“讀取-修改-寫(xiě)入”) 。 與此相關(guān)的還有另一個(gè)討厭的角度,那就是價(jià)值的波動(dòng)性。 下面的更多內(nèi)容。
這是一個(gè)菜鳥(niǎo)錯(cuò)誤,而且很難調(diào)試。 如果您確實(shí)遇到了在您的應(yīng)用程序中執(zhí)行此操作的任何人,我想請(qǐng)您幫個(gè)忙。 在數(shù)據(jù)庫(kù)中搜索“ Tal Weiss”。 如果您在那里看到我–刪除我的記錄。 我會(huì)更安全的。
同步 -這是最基本的并發(fā)習(xí)慣用法,它在讀取或?qū)懭胫禃r(shí)會(huì)阻塞所有其他線程。 當(dāng)它起作用時(shí),這是將代碼轉(zhuǎn)換為DMV行的可靠方法。
RWLock –基本Java鎖的這種稍微復(fù)雜的版本,使您可以區(qū)分更改了值并需要阻止其他線程的線程與僅讀取且不需要關(guān)鍵部分的線程。 盡管這可能更有效(假設(shè)編寫(xiě)器的數(shù)量很低),但是這是一個(gè)相當(dāng)不錯(cuò)的方法,因?yàn)樵讷@取寫(xiě)鎖時(shí),您將阻止所有其他線程的執(zhí)行。
易失性 -這個(gè)相當(dāng)容易被誤解的關(guān)鍵字實(shí)際上指示JIT編譯器取消優(yōu)化運(yùn)行時(shí)機(jī)器代碼,以便其他線程可以立即看到對(duì)該字段的任何修改。
這使一些JIT編譯器最喜歡的優(yōu)化工作失去了分配分配到內(nèi)存的順序。 再說(shuō)一次 你聽(tīng)到了 JIT編譯器可以更改對(duì)字段進(jìn)行分配的順序。 這種不可思議的小策略(也稱(chēng)為before-before )使它可以最小化程序訪問(wèn)全局堆所需的次數(shù),同時(shí)仍確保您的代碼不受其影響。 偷偷摸摸的…
那么,什么時(shí)候應(yīng)該使用易失性計(jì)數(shù)器? 如果只有一個(gè)線程在更新一個(gè)值,而有多個(gè)線程在使用它,那么這是一個(gè)非常好的策略–根本沒(méi)有爭(zhēng)用。
那么為什么不總是問(wèn)它呢? 因?yàn)楫?dāng)一個(gè)以上的線程正在更新該字段時(shí),這不能很好地工作。 由于A + = B不是原子的,因此存在覆蓋其他人的寫(xiě)入的風(fēng)險(xiǎn)。 在Java 8之前,您需要使用AtomicInteger。
AtomicInteger-這組類(lèi)使用CAS(比較和交換)處理器指令來(lái)更新計(jì)數(shù)器的值。 聽(tīng)起來(lái)不錯(cuò),不是嗎? 好吧,是的,不是。 這很有效,因?yàn)樗弥苯拥臋C(jī)器代碼指令來(lái)設(shè)置該值,而對(duì)其他線程的執(zhí)行影響最小。 缺點(diǎn)是,如果由于與另一個(gè)線程的爭(zhēng)用而無(wú)法設(shè)置該值,則必須重試。 在競(jìng)爭(zhēng)激烈的情況下,這可能會(huì)變成自旋鎖,其中線程必須不斷嘗試并在無(wú)限循環(huán)中設(shè)置該值,直到成功為止。 這不是我們想要的。 輸入帶有LongAdders的Java 8。
Java 8 Adders –這是一個(gè)非常酷的新API,我永不過(guò)時(shí)! 從使用角度來(lái)看,它與AtomicInteger非常相似。 只需創(chuàng)建一個(gè)LongAdder并使用intValue()和add()即可獲取/設(shè)置值。 魔術(shù)發(fā)生在幕后。
此類(lèi)的作用是,當(dāng)直接CAS由于爭(zhēng)用而失敗時(shí),它將增量存儲(chǔ)在為該線程分配的內(nèi)部單元對(duì)象中。 然后在調(diào)用intValue()時(shí)將待處理單元格的值加到總和上。 這減少了返回和CAS或阻止其他線程的需要。 很聰明的東西!
這么好說(shuō)吧-讓我們看看這只小狗在行動(dòng)。 我們建立了以下基準(zhǔn):將計(jì)數(shù)器重置為零,并開(kāi)始使用多個(gè)線程讀取和遞增計(jì)數(shù)器。 當(dāng)計(jì)數(shù)器達(dá)到10 ^ 8時(shí)停止。 我們?cè)?核i7處理器上運(yùn)行了基準(zhǔn)測(cè)試。
我們使用總共十個(gè)線程來(lái)運(yùn)行基準(zhǔn)測(cè)試-五個(gè)用于寫(xiě)作,五個(gè)用于閱讀,因此我們?cè)谶@里勢(shì)必會(huì)引起嚴(yán)重的爭(zhēng)論:
- 請(qǐng)注意,骯臟和易變的風(fēng)險(xiǎn)值都將覆蓋。
- 代碼在這里可用
底線
- 并發(fā)加法器潔凈室的性能比原子整數(shù)提高60-100% 。
- 除了鎖定時(shí),添加線程沒(méi)有什么區(qū)別。
- 請(qǐng)注意,使用同步鎖或RW鎖會(huì)給您帶來(lái)巨大的性能損失-慢一個(gè)數(shù)量級(jí)!
如果您已經(jīng)有機(jī)會(huì)在代碼中使用這些類(lèi),那么我很樂(lè)意聽(tīng)到。
- 補(bǔ)充閱讀– Brian Goetz關(guān)于Java并發(fā)性。
翻譯自: https://www.javacodegeeks.com/2014/04/java-8-longadders-the-right-way-to-manage-concurrent-counters.html
java 并發(fā)計(jì)數(shù)器
總結(jié)
以上是生活随笔為你收集整理的java 并发计数器_Java 8 LongAdders:管理并发计数器的正确方法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 寝室健康小常识
- 下一篇: Java单依赖性Dockerized H