Java并發(fā)總結

JMM內(nèi)存抽象

JMM定義了線程和主內(nèi)存之間的抽象關系:線程之間的共享變量存儲在主內(nèi)存中列肢,每個線程都有一個私有的本地內(nèi)存迹恐,本地內(nèi)存中存儲了該線程以讀/寫共享變量的副本逗载。
本地內(nèi)存是JMM的一個抽象概念批什,并不真實存在农曲。它涵蓋了緩存,寫緩沖區(qū),寄存器以及其他的硬件和編譯器優(yōu)化乳规。
同時JMM確保在不同的編譯器和不同的處理器平臺之上形葬,通過禁止特定類型的編譯器重排序和處理器重排序,為程序員提供一致的內(nèi)存可見性保證

所有實例域暮的、靜態(tài)域和數(shù)組元素存儲在堆內(nèi)存中笙以,堆內(nèi)存在線程之間共享
局部變量,方法定義參數(shù)和異常處理器參數(shù)不會在線程之間共享冻辩,它們不會有內(nèi)存可見性問題猖腕,也不受內(nèi)存模型的影響

重排序

在執(zhí)行程序時為了提高性能,編譯器和處理器常常會對指令做重排序

  1. 編譯器優(yōu)化的重排序:編譯器在不改變單線程程序語義的前提下恨闪,可以重新安排語句的執(zhí)行順序
  2. 指令級并行的重排序:現(xiàn)代處理器采用了指令級并行技術(Instruction-Level Parallelism倘感, ILP)來將多條指令重疊執(zhí)行。如果不存在數(shù)據(jù)依賴性咙咽,處理器可以改變語句對應機器指令的執(zhí)行順序
  3. 內(nèi)存系統(tǒng)的重排序:由于處理器使用緩存和讀/寫緩沖區(qū)老玛,這使得加載和存儲操作看上去可能是在亂序執(zhí)行
  • 對于編譯器:JMM的編譯器重排序規(guī)則會禁止特定類型的編譯器重排序
  • 對于處理器重排序:JMM的處理器重排序規(guī)則會要求java編譯器在生成指令序列時,插入特定類型的內(nèi)存屏障(memory barriers)指令钧敞,通過內(nèi)存屏障指令來禁止特定類型的處理器重排序

內(nèi)存屏障

為了保證內(nèi)存可見性蜡豹,java編譯器在生成指令序列的適當位置會插入內(nèi)存屏障指令來禁止特定類型的處理器重排序

屏障類型 指令示例 說明
LoadLoad Barriers Load1; LoadLoad; Load2 確保Load1數(shù)據(jù)的裝載,之前于Load2及所有后續(xù)裝載指令的裝載
StoreStore Barriers Store1; StoreStore; Store2 確保Store1數(shù)據(jù)對其他處理器可見(刷新到內(nèi)存)溉苛,之前于Store2及所有后續(xù)存儲指令的存儲
LoadStore Barriers Load1; LoadStore; Store2 確保Load1數(shù)據(jù)裝載镜廉,之前于Store2及所有后續(xù)的存儲指令刷新到內(nèi)存
StoreLoad Barriers Store1; StoreLoad; Load2 確保Store1數(shù)據(jù)對其他處理器變得可見(指刷新到內(nèi)存),之前于Load2及所有后續(xù)裝載指令的裝載炊昆。StoreLoad Barriers會使該屏障之前的所有內(nèi)存訪問指令(存儲和裝載指令)完成之后桨吊,才執(zhí)行該屏障之后的內(nèi)存訪問指令

StoreLoad Barriers是一個“全能型”的屏障,它同時具有其他三個屏障的效果凤巨。執(zhí)行該屏障開銷會很昂貴视乐,因為當前處理器通常要把寫緩沖區(qū)中的數(shù)據(jù)全部刷新到內(nèi)存中(buffer fully flush)

Happen-Before原則

JMM通過這個概念來闡述操作之間的內(nèi)存可見性:如果一個操作執(zhí)行的結果需要對另一個操作可見,那么這兩個操作之間必須存在happens-before關系
兩個操作既可以是在一個線程之內(nèi)敢茁,也可以是在不同線程之間

  • 程序順序規(guī)則:一個線程中的每個操作佑淀,happens- before 于該線程中的任意后續(xù)操作
  • 監(jiān)視器鎖規(guī)則:對一個監(jiān)視器鎖的解鎖,happens- before 于隨后對這個監(jiān)視器鎖的加鎖
  • volatile變量規(guī)則:對一個volatile域的寫彰檬,happens- before 于任意后續(xù)對這個volatile域的讀
  • 傳遞性:如果A happens- before B伸刃,且B happens- before C,那么A happens- before C

注意逢倍,兩個操作之間具有happens-before關系捧颅,并不意味著前一個操作必須要在后一個操作之前執(zhí)行!happens-before僅僅要求前一個操作(執(zhí)行的結果)對后一個操作可見较雕,且前一個操作按順序排在第二個操作之前(the first is visible to and ordered before the second)

并發(fā)層次

通過Volatile機制和CAS指令實現(xiàn)了多核的

緩存一致性

通過MESI協(xié)議實現(xiàn)多核間的緩存一致性
CPU所有的操作都是在其各自核的緩存內(nèi)完成的碉哑,如果不回寫挚币,其他核根本不知道變量被修改了,只有當CPU對緩存進行回寫并通過MESI協(xié)議通知時才知道

Volatile

輕量級的同步機制扣典,不會引起線程的上下文切換
當寫一個volatile變量時妆毕,JMM會把該線程對應的本地內(nèi)存中的共享變量刷新到主內(nèi)存
當讀一個volatile變量時,JMM會把該線程對應的本地內(nèi)存置為無效贮尖。線程接下來將從主內(nèi)存中讀取共享變量笛粘。即每次都從主存中讀取最新的數(shù)據(jù)
但Volatile只能保證可見性,不能保證原子性

CAS

保證多核下指令執(zhí)行的原子性湿硝,但操作的是主存薪前,直接繞過了緩存(不會觸發(fā)緩存回寫)
所以要實現(xiàn)多核間的可見性,CAS操作的變量要使用volatile修飾

原子變量實現(xiàn)

利用volatile和cas
以AtomicInteger的實現(xiàn)為例

private volatile int value;

public final int incrementAndGet() {
  // 一直循環(huán)图柏,直到更新成功為止
  for (;;) {
    // 獲取當前值
    int current = get();
    int next = current + 1;
    // 使用cas原子性更新
    if (compareAndSet(current, next))
      return next;
    }
}

// volatile修飾序六,每次都讀取最新的值
public final int get() {
  return value;
}

AQS實現(xiàn)

本質(zhì)就是狀態(tài)+等待鏈表
通過一個內(nèi)部的Int變量來代表狀態(tài)任连,狀態(tài)的值代表通過還是等待
如果等待蚤吹,則提供一個非阻塞的等待鏈表(通過CAS來實現(xiàn)節(jié)點的增刪),同時在釋放時随抠,喚醒等待的線程
AQS是一個基礎類裁着,所有需要阻塞等待的,都可以使用其特性來實現(xiàn)

鎖實現(xiàn)

繼承于AQS拱她,對于互斥鎖二驰,狀態(tài)0為則沒有通過,并設置為1秉沼,否則阻塞等待

public void lock() {
  sync.lock();
}

// NonFair為例
final void lock() {
  // 設置狀態(tài)成功(0變成1)桶雀,成功獲取鎖
  if (compareAndSetState(0, 1))
    setExclusiveOwnerThread(Thread.currentThread());
  else
    // 再嘗試申請,失敗則等待
    acquire(1);
}

// AQS
public final void acquire(int arg) {
  // 如果嘗試申請失敗唬复,則將當前線程加入到等待隊列中
  if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
    selfInterrupt();
}

final boolean nonfairTryAcquire(int acquires) {
  final Thread current = Thread.currentThread();
  // 再次嘗試申請一次
  int c = getState();
  if (c == 0) {
    if (compareAndSetState(0, acquires)) {
      setExclusiveOwnerThread(current);
      return true;
    }
  }
  // 如果是當前線程獲取的鎖矗积,則直接增加次數(shù)即可
  else if (current == getExclusiveOwnerThread()) {
    int nextc = c + acquires;
    if (nextc < 0) // overflow
      throw new Error("Maximum lock count exceeded");
    setState(nextc);
    return true;
  }
  return false;
}

并發(fā)數(shù)據(jù)結構

這里不對每個數(shù)據(jù)結構的實現(xiàn)方式做詳細描述,只簡單地列一下每個數(shù)據(jù)結構的大概使用方式

數(shù)據(jù)結構 說明
SynchronousQueue 并不是一個隊列敞咧,可以看成size=1的隊列棘捣,一次只能放入一個元素
PriorityBlockingQueue 優(yōu)先級隊列
LinkedBlockingQueue 鏈表隊列,隊列為空時需要等待休建,適合生產(chǎn)者-消費者模型
LinkedBlockingDeque 鏈表的雙端隊列
DelayQueue 延遲隊列
CopyOnWriteArraySet 修改時進行復制的集合
CopyOnWriteArrayList 修改時進行復制的隊列
ConcurrentSkipListSet 有序的Set
ConcurrentSkipListMap 有序的Map
ConcurrentLinkedQueue 并發(fā)隊列乍恐,添加、獲取都不會等待
ConcurrentHashMap 并發(fā)HashMap
ArrayBlockingQueue 數(shù)組隊列测砂,有界

參考

深入理解Java內(nèi)存模型(一)——基礎
深入理解Java內(nèi)存模型(四)——volatile
聊聊并發(fā)(一)——深入分析Volatile的實現(xiàn)原理
深入理解Java內(nèi)存模型(五)——鎖

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末茵烈,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子砌些,更是在濱河造成了極大的恐慌呜投,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異宙彪,居然都是意外死亡矩动,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門释漆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來悲没,“玉大人,你說我怎么就攤上這事男图∈咀耍” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵逊笆,是天一觀的道長栈戳。 經(jīng)常有香客問我,道長难裆,這世上最難降的妖魔是什么子檀? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮乃戈,結果婚禮上褂痰,老公的妹妹穿的比我還像新娘。我一直安慰自己症虑,他們只是感情好缩歪,可當我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著谍憔,像睡著了一般匪蝙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上习贫,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天逛球,我揣著相機與錄音,去河邊找鬼沈条。 笑死需忿,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的蜡歹。 我是一名探鬼主播屋厘,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼月而!你這毒婦竟也來了汗洒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤父款,失蹤者是張志新(化名)和其女友劉穎溢谤,沒想到半個月后瞻凤,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡世杀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年阀参,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瞻坝。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡蛛壳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出所刀,到底是詐尸還是另有隱情衙荐,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布浮创,位于F島的核電站忧吟,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏斩披。R本人自食惡果不足惜溜族,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望雏掠。 院中可真熱鬧斩祭,春花似錦劣像、人聲如沸乡话。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绑青。三九已至,卻和暖如春屋群,著一層夾襖步出監(jiān)牢的瞬間闸婴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工芍躏, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留邪乍,地道東北人。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓对竣,卻偏偏與公主長得像庇楞,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子否纬,可洞房花燭夜當晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容