談論Java原子變量和同步的效率 -- 顛覆你的生活

我們認為,由于思維定式原子變量總是比同步運行的速度更快,我想是這樣也已經,直到實現了ID在第一次測試過程生成器不具有在這樣一個迷迷糊糊的東西。


測試代碼:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;public class ConcurrentAdder {private static final AtomicInteger ATOMIC_INTEGER = new AtomicInteger(0);private static int I = 0;private static final Object o = new Object();private static volatile long start;public static void main(final String[] args) {//每一個線程運行多少次累加int round = 10000000;//線程個個數int threadn = 20;start = System.currentTimeMillis();atomicAdder(threadn, round);//syncAdder(threadn, round);}static void atomicAdder(int threadn, int addTimes) {int stop = threadn * addTimes;List<Thread> list = new ArrayList<Thread>();for (int i = 0; i < threadn; i++) {list.add(startAtomic(addTimes, stop));}for (Thread each : list) {each.start();}}static Thread startAtomic(final int addTimes, final int stop) {Thread ret = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < addTimes; i++) {int v = ATOMIC_INTEGER.incrementAndGet();if (stop == v) {System.out.println("value:" + v);System.out.println("elapsed(ms):" + (System.currentTimeMillis() - start));System.exit(1);}}}});ret.setDaemon(false);return ret;}static void syncAdder(int threadn, int addTimes) {int stop = threadn * addTimes;List<Thread> list = new ArrayList<Thread>();for (int i = 0; i < threadn; i++) {list.add(startSync(addTimes, stop));}for (Thread each : list) {each.start();}}static Thread startSync(final int addTimes, final int stop) {Thread ret = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < addTimes; i++) {synchronized (o) {I++;if (stop == I) {System.out.println("value:" + I);System.out.println("elapsed(ms):" + (System.currentTimeMillis() - start));System.exit(1);}}}}});ret.setDaemon(false);return ret;}
}


這是一個非常easy的累加器,N個線程并發累加,每一個線程累加R次。


分別凝視

atomicAdder(threadn, round);//原子變量累加
syncAdder(threadn, round);//同步累加
中的一行運行還有一行


筆者機器的配置:i5-2520M 2.5G 四核

N=20

R=10000000

結果:

原子累加:15344 ms

同步累加:10647 ms


問題出來了,為什么同步累加會比原子累加要快50%左右?



@ 我們知道java加鎖的過程是(內置sync和顯式lock類似),要加鎖的線程檢查下鎖是否被占用。假設被占用則增加到目標鎖的等待隊列。假設沒有則。加鎖。


這里我們每一個線程獲取到鎖累加之后就立刻又去獲取鎖,這時其它線程還沒有被喚醒。鎖又被當前線程拿到了。

這也就是非公平鎖可能造成的饑餓問題。


可是這一個原因不能解釋50%的性能提升?理論上。在一個絕對時間。總有一個線程累加成功,那么兩種累加器的耗時應該近似才對。


那么是有什么提升了同步累加的性能。或者是什么減少了原子累加的性能?


@接下來筆者分別perf了一下兩種累加器的運行過程:

第一次運行的是原子累加器,第二次運行的同步累加器。


wxf@pc:/data$ perf stat -e cs -e L1-dcache-load-misses java ConcurrentAdder
value:100000000
elapsed(ms):8580Performance counter stats for 'java ConcurrentAdder 1 100 1000000':21,841 cs                                                          233,140,754 L1-dcache-load-misses                                       8.633037253 seconds time elapsed

wxf@pc:/data$ perf stat -e cs -e L1-dcache-load-misses java ConcurrentAdder
value:100000000
elapsed(ms):5749Performance counter stats for 'java ConcurrentAdder 2 100 1000000':55,522 cs                                                          28,160,673 L1-dcache-load-misses                                       5.811499179 seconds time elapsed


我們能夠看出,同步累加的上下文切換是要比原子累加多。這個能夠理解,加鎖本身就會添加線程的切換。

再看,原子累加器的L1緩存失效比同步累加器高一個數量級


筆者茅塞頓開,原子操作會導致緩存一致性問題。從而導致頻繁的緩存行失效。緩存一致性協議MESI見:http://en.wikipedia.org/wiki/MESI_protocol

可是這時同步累加器在一個CPU周期內重復的獲取鎖操作。緩存并沒有失效。

再把每次累加的線程ID輸出來,會發現。原子累加的線程分布要分散非常多。


回到問題上來。為什么我們會一直覺得原子操作比加鎖要快呢?文中的樣例是非常特別非常特別的,正常業務場景下,我們累加過后,要經過非常多業務代碼邏輯才會再次去累加,這里已經跨過非常多個CPU時間片了。從而同步累加器非常難一直獲取到鎖。這中情況下,同步累加器即會有等待加鎖的性能損失還會有緩存一致性帶來的性能損失。

所以在一般的情況下,同步累加器會慢非常多。





版權聲明:本文博客原創文章。博客,未經同意,不得轉載。

轉載于:https://www.cnblogs.com/hrhguanli/p/4740290.html

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

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

相關文章

總結從linux - windows 上(GCC與MSVC 2015) 移植C或者C++代碼時候遇到的編譯和鏈接問題

解決辦法和修改辦法的總體思想 1、在代碼的語義和功能不變的情況下&#xff0c;進行c->c的轉變&#xff0c;消除編譯器的跨平臺編譯報錯問題&#xff1b; 2、遇到編譯問題&#xff0c;先看編譯器提示信息&#xff0c;然后逐個排查和定位&#xff0c;去解決問題。 1、當遇到…

1 week110的zookeeper的安裝 + zookeeper提供少量數據的存儲

下面是在weekend110上的zookeeper的安裝 在此之前&#xff0c;先進行快照備份。 在這里&#xff0c;為了后續的zookeeper配置需要&#xff0c;在/home/hadoop/下&#xff0c;新建data目錄&#xff0c;再新建zookeeper-3.4.6目錄&#xff0c;再新建zkdata目錄。 默認情況下&…

學習筆記(46):Python實戰編程-protocol

立即學習:https://edu.csdn.net/course/play/19711/343108?utm_sourceblogtoedu 1.protocol協議&#xff1a;主要用于窗體關閉監聽&#xff0c;通過這個協議可以在窗體關閉前提示是否真的要關閉&#xff0c;這樣可以防止用戶誤觸導致數據的丟失 2.關鍵代碼 #----------------…

linux yum命令詳解

yum&#xff08;全稱為 Yellow dog Updater, Modified&#xff09;是一個在Fedora和RedHat以及SUSE中的Shell前端軟件包管理器。基於RPM包管理&#xff0c;能夠從指定的服務器自動下載RPM包并且安裝&#xff0c;可以自動處理依賴性關系&#xff0c;并且一次安裝所有依賴的軟體包…

linux配置Java環境變量(詳細)

一. 解壓安裝jdk 在shell終端下進入jdk-6u14-linux-i586.bin文件所在目錄&#xff0c; 執行命令 ./jdk-6u14-linux-i586.bin 這時會出現一段協議&#xff0c;連繼敲回車&#xff0c;當詢問是否同意的時候&#xff0c;輸入yes&#xff0c;回車。之后會在當前目錄下生成一個jdk1.…

在c++代碼中關閉和啟動另外一個pid進程號,共享內存數據使用

char * of_program_name "./AB"; 1、 pid getPidFromName(of_program_name); if (pid > 0) { ERROR("AB process still exispid %d\n", pid); if (kill(pid, SIGKILL)) //殺死進程 { fprintf(std…

提速XP操作系統

1、加速網上鄰居 在Windows XP中訪問網上鄰居是相當惱人的&#xff0c;系統會搜索自己的共享目錄和可作為網絡共享的打印機以及計劃任務中和網絡相關的計劃任務&#xff0c;然后才顯示出來&#xff0c;顯然這樣速度就會比Windows 9x中慢很多。其實這些功能我們并沒有使用上&…

學習筆記(47):Python實戰編程-pack布局

立即學習:https://edu.csdn.net/course/play/19711/343109?utm_sourceblogtoedu pack布局&#xff1a; 1&#xff09;最常用的布局&#xff0c;順序排列布局方法 2&#xff09;完成了簡單的組件位置碼放&#xff0c;但如果內部布局處理機制跟不上的話&#xff0c;也可能會達…

ASP.NET Web API 應用教程(一) ——數據流使用

相信已經有很多文章來介紹ASP.Net Web API 技術&#xff0c;本系列文章主要介紹如何使用數據流&#xff0c;HTTPS&#xff0c;以及可擴展的Web API 方面的技術&#xff0c;系列文章主要有三篇內容。 主要內容如下&#xff1a; I 數據流 II 使用HTTPS III 可擴展的Web API 文檔…

JAVA類的構造方法

1,構造方法沒有返回類型, 定義: []public] 方法名() {} 2,一個構造方法如果想調用同一類中的另一個構造方法,只能調用一個,并且要放在構造方法第一行 3,用this調用,如 1 public person(int i, int j) { 2 this(); //調用另一個構造方法,且放在第一行 3 } 轉載于:https://www…

將字符串和數字合并動態寫入

sprintf(tmpTime, "/media/AIlog/%02d_%02d_%.txt",100,200);

windows安裝64位Pygame方法

因為官方網站http://www.pygame.org/download.shtml并沒有提供64位的pygame&#xff0c;所以要找其他網站制作的64位安裝文件。 如果你已經在你的64位開發環境中安裝了32位的pygame&#xff0c; 那么在導入pygame包的時候&#xff0c; 會有提示&#xff1a; ImportError: DLL l…

學習筆記(48):Python實戰編程-grid布局

立即學習:https://edu.csdn.net/course/play/19711/343110?utm_sourceblogtoedu grid布局&#xff1a;根據表結構進行的布局&#xff0c;索引為0行0列開始&#xff0c;最具代表性的即使電腦計算器的數字和符號按鍵布局 組件.grid(row 行索引號 &#xff0c; column 列索引號…

為什么“三次握手,四次揮手”?

前言&#xff1a; 如果你說你懂IT,你懂計算機網絡&#xff0c;那么你會怎么解釋“三次握手&#xff0c;四次揮手”&#xff1f; ---------------- 1.TCP報文段首部格式&#xff1a; 2.TCP建立連接圖 -------------------------------------------- 符號說明 seq:"sequance…

學習筆記(49):Python實戰編程-place布局

立即學習:https://edu.csdn.net/course/play/19711/343111?utm_sourceblogtoedu 1.place布局&#xff1a; 1&#xff09;最靈活的布局方式&#xff0c;是根據坐標點來進行組件的位置布局的 2&#xff09;確定坐標點后&#xff0c;組件從坐標點開始展開&#xff0c;即以指定…

cobbler基礎安裝

epel源安裝rpm -ivh epel-release-6-8.noarch.rpmyum -y install cobbler httpd rsync tftp-server xinetd dhcp cobbler-web koan pykickstart* fence-agents* debmirror syslinux$$$$$$$$$$$啟動cobbler服務/etc/init.d/cobblerd start$$$$$$$$啟動httpd服務/etc/init.d/http…

[ActionScript 3.0] NetConnection建立客戶端與服務器的雙向連接

一個客戶端與服務器之間的接口測試的工具 <?xml version"1.0" encoding"utf-8"?> <!--- - - - - - - - - - - - - - - - - - - - - - - - - * author:Frost.Yen * E-mail:871979853qq.com * create:2016-7-13上午12:10:20 - - - - - - - - - - …

C++ share_prt 簡單設計和實現

C 比較煩惱的是內存的管理&#xff0c;new是簡單&#xff0c;不夠&#xff0c;delete偶爾就會忘記。或者說&#xff0c;出現&#xff0c;多個對象共享多一個內存&#xff0c;一個delete以后&#xff0c;導致其他也不能用的不良情況&#xff0c;所以就跑出了一個智能指針來進行管…

PID控制無人機

1、有效的辦法就是&#xff0c;根據距離目標值的遠近來調節云臺的速度&#xff0c;離得遠就轉快一些&#xff0c;離得近就慢一些。這樣就需要我們實時獲取云臺當前的實際角度&#xff0c;然后和目標角度做對比&#xff0c;根據差值來動態地調整云臺的速度和方向。 2、根據無人…

學習筆記(50):Python實戰編程-Frame

立即學習:https://edu.csdn.net/course/play/19711/343112?utm_sourceblogtoedu 1.Frame:是內嵌布局管理器&#xff0c;針對不同功能的組件進行區域劃分&#xff0c;在各自的區域內可以使用不同的布局&#xff0c;但是總的frame布局方式還是一致的 2.以計算器為例 步驟&#…