BiBi - JVM -13- 并發(fā)

From:深入理解Java虛擬機(jī)

基于【高速緩存】的存儲交互很好地解決了處理器與內(nèi)存的速度矛盾,同時也引入了一個問題:緩存一致性留瞳。在多處理器系統(tǒng)中,每個處理器都有自己的高速緩存岳守,而它們又共享同一個主內(nèi)存,當(dāng)多個處理器的運(yùn)算任務(wù)涉及到同一塊主內(nèi)存區(qū)域時樊卓,將會導(dǎo)致數(shù)據(jù)緩存不一致密强。所以,要遵守緩存一致性協(xié)議患雏,在讀寫的時候要根據(jù)協(xié)議來進(jìn)行操作。

Java內(nèi)存模型 - JMM

Java虛擬機(jī)通過Java內(nèi)存模型【Java Memory Model罢维,JMM】來屏蔽各種硬件和操作系統(tǒng)的內(nèi)存訪問差異淹仑,以實現(xiàn)讓Java程序在各種平臺下都能達(dá)到一致的內(nèi)存訪問效果》畏酰【C/C++直接使用物理硬件和操作系統(tǒng)的內(nèi)存模型】

JVM可以利用硬件的特性有:寄存器匀借、高速緩存、指令集中某些特有的指令平窘。
內(nèi)存間交互的8個操作:
lock
unlock
read
load
use
assign
store
它們都是原子的吓肋、不可再分的【對于long和double類型的變量JVM允許為非原子操作,但現(xiàn)在的商用JVM都選擇把64位數(shù)據(jù)的讀寫作為原子操作來對待瑰艘。所以是鬼,一般不需要把long和double專門聲明為volatile】。

把一個變量從主內(nèi)存復(fù)制到線程的工作內(nèi)存紫新,執(zhí)行read >> load均蜜;把一個變量從線程的工作內(nèi)存同步回主內(nèi)存,執(zhí)行store >> write芒率。

注意:Java內(nèi)存模型只要求上述兩個操作必須按照順序執(zhí)行兆龙,而沒有保證是連續(xù)執(zhí)行的。所以敲董,可能出現(xiàn)順序為:read a、read b慰安、load b腋寨、load a。

對一個變量執(zhí)行unlock操作之前化焕,必須先把此變量同步回主內(nèi)存中萄窜。

volatile

volatile變量在各個線程的工作內(nèi)存中不存在一致性問題,對所有線程都可見。但Java里面的運(yùn)算并非原子操作【如:++i】查刻,導(dǎo)致volatile變量的運(yùn)算在并發(fā)下一樣不是安全的键兜。

一條字節(jié)碼指令,不能代表執(zhí)行這條指令是一個原子操作穗泵。

volatile變量能夠禁止指令重排序優(yōu)化普气。

對一個volatile變量的寫操作【先行發(fā)生】于后面對這個變量的讀操作。
問題:線程A先調(diào)用setValue(7)佃延,然后線程B調(diào)用同一個對象的getVale()现诀,那么線程B會收到什么值?

  private int value = 0;

  public int getValue() {
    return value;
  }

  public void setValue(int value) {
    this.value = value;
  }

答案:不確定履肃,0 或 7仔沿。如果改為private int volatile value = 0;結(jié)果一定為7。

final的可見性:被final修飾的字段在構(gòu)造器中一旦初始化完成尺棋,并且構(gòu)造器沒有把this引用傳遞出去封锉,即沒有發(fā)生逃逸,那在其他線程中就能看見final字段膘螟。【因為該字段以后只能被讀成福,不能再被修改】【不可變對象一定是線程安全的,前提沒有this逃逸】

不可變對象萍鲸,最簡單的就是把對象中的成員都設(shè)為final闷叉,如:Integer中的private final int value;

線程

線程可以把一個進(jìn)程的資源分配和執(zhí)行調(diào)度分開脊阴,各個線程既可以共享進(jìn)程資源【如:內(nèi)存地址握侧、文件IO】,又可以獨立調(diào)度嘿期。

Java虛擬機(jī)的線程模型:JDK1.2之前是【用戶線程實現(xiàn)的】品擎,在JDK1.2之后,線程模型替換為【基于操作系統(tǒng)原生線程模型】來實現(xiàn)备徐。如:Sun JDK萄传,在Widows和Linux都是使用一對一的線程模型來實現(xiàn)的,即一條Java線程就映射到一條輕量級進(jìn)程中蜜猾,因為Widows和Linux系統(tǒng)提供的線程模型就是一對一的秀菱。

由于Java的線程是映射到操作系統(tǒng)原生線程上的,如果要阻塞或喚醒一個線程蹭睡,都需要操作系統(tǒng)來幫忙完成衍菱,這就需要從用戶狀態(tài)轉(zhuǎn)換到內(nèi)核狀態(tài),因此狀態(tài)轉(zhuǎn)換需要耗費(fèi)很多的處理器時間肩豁。

線程調(diào)度

協(xié)同式線程調(diào)度和搶占式線程調(diào)度脊串。
協(xié)調(diào)式:線程的執(zhí)行時間由線程本身來控制辫呻,線程把自己的工作執(zhí)行完后,要主動通知系統(tǒng)切換到另外一個線程上琼锋》殴耄【沒有線程同步問題】。缺點:當(dāng)一個線程編寫有錯誤缕坎,導(dǎo)致一直不告訴系統(tǒng)進(jìn)行線程切換怖侦,那就會一直阻塞。即一個進(jìn)程堅持不讓出CUP執(zhí)行時間念赶,使整個系統(tǒng)崩潰础钠。

搶占式【Java使用的線程調(diào)度方式】:每個線程由系統(tǒng)來分配執(zhí)行時間,線程的切換不由線程本身決定叉谜。此種情況旗吁,線程的執(zhí)行時間是系統(tǒng)可控的,不會出現(xiàn)讓一個線程導(dǎo)致阻塞整個進(jìn)程停局。

線程安全

對于線程安全的容器很钓,使用其方法時,也要注意同步問題董栽,如下:Vector是安全容器码倦,remove()和get()方法都是同步方法。

public void testSync() {
  final Vector vector = new Vector(10);
  new Thread(new Runnable() {
    @Override
    public void run() {
      for (int i = 0; i < vector.size(); ++i) {
        vector.remove(i);
      }
    }
  }).start();
  new Thread(new Runnable() {
    @Override
    public void run() {
      for (int i = 0; i < vector.size(); ++i) {
        vector.get(i);
      }
    }
  }).start();
}

上面的例子锭碳,會產(chǎn)生數(shù)組越界問題袁稽,解決方案如下:

public void testSync() {
  final Vector vector = new Vector(10);
  new Thread(new Runnable() {
    @Override
    public void run() {
      synchronized (vector) {
        for (int i = 0; i < vector.size(); ++i) {
          vector.remove(i);
        }
      }
    }
  }).start();
  new Thread(new Runnable() {
    @Override
    public void run() {
      synchronized (vector) {
        for (int i = 0; i < vector.size(); ++i) {
          vector.get(i);
        }
      }
    }
  }).start();
}

同步:互斥/阻塞同步、非阻塞同步
在JDK1.6之后擒抛,synchronized與ReentrantLock的性能基本上完全持平推汽,因此若基于性能因素而言,不再選擇ReentrantLock了歧沪,而是偏向于使用synchronized歹撒。

由于阻塞同步需要:加鎖、用戶狀態(tài)內(nèi)核轉(zhuǎn)換诊胞、維護(hù)鎖計數(shù)器暖夭、檢查是否有被阻塞的線程需要喚醒等,這些都會造成性能問題撵孤,所以這是一種悲觀的并發(fā)策略【原因:總是認(rèn)為只要不做同步處理迈着,那就肯定會出現(xiàn)問題】。

隨著硬件指令集的發(fā)展邪码,出現(xiàn)了【基于沖突檢測】的樂觀并發(fā)策略寥假,即非阻塞同步
定義:先進(jìn)行操作霞扬,如果沒有其他線程爭用共享數(shù)據(jù),那操作就成功了;如果共享數(shù)據(jù)有爭用喻圃,產(chǎn)生了沖突萤彩,那就再采用其他的補(bǔ)償措施,如:不斷地重試斧拍,直到成功為止雀扶。該種方式不需要把線程掛起。

  • 為什么非阻塞同步是隨著【硬件指令集的發(fā)展】才實現(xiàn)呢肆汹?

因為操作和沖突檢測這兩個步驟需要具備原子性愚墓,那靠什么來保證呢?如果這里再使用互斥同步來保證就失去意義了昂勉,所以只能依靠硬件來完成這件事情浪册,硬件保證一個從語義上看起來需要多次操作的行為只通過一條處理器指令就能完成,如:比較并替換【Compare And Swap岗照,CAS】村象。

體會CAS的背景和應(yīng)用。

代碼體會:

public final int incrementAndGet() {
  for (; ; ) {
    int current = get();
    int next = current + 1;
    if (compareAndSet(current, next)) {
      return next;
    }
  }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末攒至,一起剝皮案震驚了整個濱河市厚者,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌迫吐,老刑警劉巖库菲,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異志膀,居然都是意外死亡熙宇,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進(jìn)店門梧却,熙熙樓的掌柜王于貴愁眉苦臉地迎上來奇颠,“玉大人,你說我怎么就攤上這事放航×揖埽” “怎么了?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵广鳍,是天一觀的道長荆几。 經(jīng)常有香客問我,道長赊时,這世上最難降的妖魔是什么吨铸? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮祖秒,結(jié)果婚禮上诞吱,老公的妹妹穿的比我還像新娘舟奠。我一直安慰自己,他們只是感情好房维,可當(dāng)我...
    茶點故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布沼瘫。 她就那樣靜靜地躺著,像睡著了一般咙俩。 火紅的嫁衣襯著肌膚如雪耿戚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天阿趁,我揣著相機(jī)與錄音膜蛔,去河邊找鬼。 笑死脖阵,一個胖子當(dāng)著我的面吹牛皂股,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播独撇,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼屑墨,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了纷铣?” 一聲冷哼從身側(cè)響起卵史,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎搜立,沒想到半個月后以躯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡啄踊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年忧设,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片颠通。...
    茶點故事閱讀 38,626評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡址晕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出顿锰,到底是詐尸還是另有隱情谨垃,我是刑警寧澤,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布硼控,位于F島的核電站刘陶,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏牢撼。R本人自食惡果不足惜匙隔,卻給世界環(huán)境...
    茶點故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望熏版。 院中可真熱鬧纷责,春花似錦捍掺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至饵史,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間胜榔,已是汗流浹背胳喷。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留夭织,地道東北人吭露。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像尊惰,于是被迫代替她去往敵國和親讲竿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,494評論 2 348