【Java重拾】線程安全性概述

并發(fā)概述

學(xué)過單片機(jī),微機(jī)原理的同學(xué)都知道。在單片機(jī)構(gòu)成的最小系統(tǒng)中辟拷,程序是按照從上往下執(zhí)行的,任何時(shí)間點(diǎn)环凿,任何時(shí)間點(diǎn)執(zhí)行單元中只會(huì)存在一條指令,這樣帶來最直接的問題就是資源利用率低下放吩。例如程序因需要等待IO輸入而進(jìn)行循環(huán)等待智听,此時(shí)CPU本應(yīng)空閑下來卻因?yàn)樾枰却脩糨斎攵鵁o(wú)法真正去執(zhí)行其他程序邏輯塊,造成CPU資源浪費(fèi)渡紫。為此CPU提供了中斷機(jī)制(此中斷非Java的中斷)來使得CPU可以在等待IO輸入時(shí)可以執(zhí)行其他指令到推,而在IO輸入時(shí)利用中斷機(jī)制打斷現(xiàn)有的程序邏輯進(jìn)入中斷,執(zhí)行完中斷程序后再跳回原先的程序邏輯惕澎,從而提高了CPU的資源利用率莉测。這是相對(duì)底層的做法,在操作系統(tǒng)層面唧喉,一般通過粗粒的的時(shí)間分片捣卤,控制CPU在不同的時(shí)間段(很小)內(nèi)執(zhí)行不同的程序邏輯八孝,以達(dá)到在用戶無(wú)法感知的情況下“同時(shí)”運(yùn)行多個(gè)程序董朝,這就是我們說的多進(jìn)程/多線程。雖然這樣提高了系統(tǒng)資源的利用率干跛,但若在進(jìn)程或線程間存在數(shù)據(jù)交換子姜,就會(huì)隨之帶來讓人頭疼的并發(fā)問題。
從一只Java程序猿觸發(fā)楼入,本文將介紹著重介紹Java中的線程安全概念哥捕。

1.Java中并發(fā)為什么會(huì)帶來安全性問題

Java采用多線程的形式為程序提供并發(fā)執(zhí)行方案。在同一時(shí)間可能存在多條線程同時(shí)訪問同一個(gè)變量并對(duì)其進(jìn)行值修改嘉熊。我們知道Java在編譯后是以字節(jié)碼的形式供JVM執(zhí)行遥赚,而一般情況下在對(duì)變量進(jìn)行操作時(shí)會(huì)經(jīng)歷入棧,運(yùn)算阐肤,出棧的操作鸽捻。此時(shí)如果有兩個(gè)線程同時(shí)進(jìn)行入棧,再運(yùn)算之后將結(jié)果出棧推回變量泽腮,就會(huì)造成計(jì)算結(jié)果無(wú)法預(yù)估御蒲,因?yàn)榻Y(jié)果取決于執(zhí)行較慢的那個(gè)線程。超減問題便是在高并發(fā)情況下的安全問題之一诊赊。

public class IdentifyGenerator{
  private int v;
  public int next(){
    //這里不使用return ++v或 return v++主要是怕難以理解,注意兩者的區(qū)別
    v = v + 1;
    return v;
  }
}

上述代碼在多線程情境下厚满,會(huì)存在線程安全問題,原因在于v=v+1并不是一步操作碧磅。而是先讀取v的值碘箍,再+1遵馆,再寫回v中。使用命令javap -c 查看如下:

public class IdentifyGenerator{
  public IdentifyGenerator();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public int get();
    Code:
       0: aload_0
       1: aload_0
       2: getfield      #2                  // 讀取變量v的值入棧
       5: iconst_1                          // 常量1
       6: iadd                                 // 棧頂加法運(yùn)算
       7: putfield      #2                  // 寫回變量v
      10: aload_0
      11: getfield      #2                  // Field v:I
      14: ireturn
}

多線程下線程交替執(zhí)行

如上圖丰榴,在線程交替執(zhí)行的情況下货邓,我們的唯一標(biāo)示生成器可能返回兩次1,這種情況下四濒,就認(rèn)為我們寫的這個(gè)類不是線程安全的换况。

2.什么是線程安全

當(dāng)多個(gè)線程同時(shí)訪問某個(gè)資源(類)時(shí),不管JVM對(duì)線程采用何種調(diào)度方式和交替執(zhí)行順序盗蟆,并且在主調(diào)代碼中不需要額外的同步或協(xié)同戈二,這個(gè)類都能表現(xiàn)出正確的行為,那么就說該類是線程安全的喳资。

3.如何實(shí)現(xiàn)線程安全

3.1原子性

在1節(jié)中我們知道造成線程安全問題觉吭,是由線程交替執(zhí)行導(dǎo)致我們的關(guān)鍵性操作被拆分導(dǎo)致的。所以如果若我們能將關(guān)鍵性操作打包成一個(gè)操作仆邓,那么就可以規(guī)避該問題了鲜滩。我們稱這種打包后的,不可拆分的操作為原子操作节值。
1節(jié)中這種由于線程交替執(zhí)行而出現(xiàn)不正確結(jié)果绒北,我們稱之為競(jìng)態(tài)條件。常見的競(jìng)態(tài)條件類型有:先檢查后操作察署,延遲初始化(其實(shí)和先檢查后操作差不多闷游。例子:懶漢單例模式)。

3.2可見性

可見性是指當(dāng)多個(gè)線程訪問同一個(gè)變量時(shí)贴汪,一個(gè)線程修改了這個(gè)變量的值脐往,其他線程能夠立即看得到修改的值。
Java線程在讀取變量時(shí)扳埂,會(huì)先從主存將變量讀入到CPU高速緩存中业簿,線程在對(duì)變量進(jìn)行操作時(shí),都是先在高速緩存上的這個(gè)副本進(jìn)行操作后阳懂,再寫回主存梅尤,從而導(dǎo)致了可見性問題。Java采用volatile關(guān)鍵字來保證可見性岩调。當(dāng)一個(gè)共享變量被volatile修飾時(shí)巷燥,它會(huì)保證修改的值會(huì)立即被更新到主存,當(dāng)有其他線程需要讀取時(shí)号枕,它會(huì)去內(nèi)存中讀取新值缰揪。

4.Java中的加鎖機(jī)制

4.1Java同步代碼塊

Java提供了同步代碼塊的加鎖機(jī)制來實(shí)現(xiàn)原子操作,同步代碼塊包括兩個(gè)部分葱淳,一個(gè)是作為鎖的對(duì)象引用钝腺,一個(gè)是被該鎖保護(hù)的代碼塊抛姑。Java同步鎖相當(dāng)于一種互斥體,同一時(shí)間只能有一個(gè)線程可以持有同一對(duì)象的鎖艳狐,未持有該對(duì)象鎖的線程會(huì)被阻塞而不能進(jìn)入代碼塊定硝。
值得注意的是,即使我們采用了同步代碼塊毫目,我們的類或方法并不一定是線程安全的蔬啡。要保證線程安全,我們需要在同步代碼塊(即單個(gè)原子操作)中更新所有相關(guān)的狀態(tài)變量蒜茴。

4.2可重入

Java同步鎖是一種可重入鎖星爪,可重入意味著獲取了鎖的線程在試圖重新獲取該鎖時(shí)浆西,是成功的粉私。即鎖的操作粒度是線程而不是代碼塊。重入鎖的一種實(shí)現(xiàn)方式是近零,給每個(gè)鎖增加一個(gè)獲取計(jì)數(shù)值和一個(gè)所有線程诺核,當(dāng)有線程進(jìn)入成功獲取鎖進(jìn)入同步代碼時(shí),便是計(jì)數(shù)器加1久信,退出代碼塊則減1.當(dāng)計(jì)數(shù)器為0時(shí)認(rèn)為沒有線程獲取到該鎖窖杀。

4.3活躍性問題和性能問題

同步鎖保證了Java在多線程環(huán)境下的安全性,但同時(shí)也降低了程序的并發(fā)能力裙士。倘若濫用同步鎖入客,由于同步代碼塊在同一時(shí)間只能有一個(gè)線程在執(zhí)行,那么極端情況下可能造成即使采用了多線程腿椎,實(shí)際上JVM中只存在一個(gè)線程在執(zhí)行桌硫。

public class Server extends Thread{
  ...

  public synchronized void doSomething(){
    ...
  }

  public void run(){
    doSomething();
  }
  
  public static void main(String[] args){
    new Server().start();
    new Server().start();
    new Server().start();
  }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市啃炸,隨后出現(xiàn)的幾起案子铆隘,更是在濱河造成了極大的恐慌,老刑警劉巖南用,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件膀钠,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡裹虫,警方通過查閱死者的電腦和手機(jī)肿嘲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來筑公,“玉大人睦刃,你說我怎么就攤上這事∈ǎ” “怎么了涩拙?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵际长,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我兴泥,道長(zhǎng)工育,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任搓彻,我火速辦了婚禮如绸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘旭贬。我一直安慰自己怔接,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布稀轨。 她就那樣靜靜地躺著扼脐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪奋刽。 梳的紋絲不亂的頭發(fā)上瓦侮,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音佣谐,去河邊找鬼肚吏。 笑死,一個(gè)胖子當(dāng)著我的面吹牛狭魂,可吹牛的內(nèi)容都是我干的罚攀。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼雌澄,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼斋泄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起掷伙,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤是己,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后任柜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體卒废,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年宙地,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了摔认。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡宅粥,死狀恐怖参袱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤抹蚀,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布剿牺,位于F島的核電站,受9級(jí)特大地震影響环壤,放射性物質(zhì)發(fā)生泄漏晒来。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一郑现、第九天 我趴在偏房一處隱蔽的房頂上張望湃崩。 院中可真熱鬧,春花似錦接箫、人聲如沸攒读。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)薄扁。三九已至,卻和暖如春瞎领,著一層夾襖步出監(jiān)牢的瞬間泌辫,已是汗流浹背随夸。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來泰國(guó)打工九默, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人宾毒。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓驼修,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親诈铛。 傳聞我的和親對(duì)象是個(gè)殘疾皇子乙各,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

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

  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司幢竹,掛了不少耳峦,但最終還是拿到小米、百度焕毫、阿里蹲坷、京東、新浪邑飒、CVTE循签、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,207評(píng)論 11 349
  • 本文基于周志明的《深入理解java虛擬機(jī) JVM高級(jí)特性與最佳實(shí)踐》所寫。特此推薦疙咸。 衡量一個(gè)服務(wù)性能的高低好壞县匠,...
    陽(yáng)光的技術(shù)小棧閱讀 1,066評(píng)論 0 3
  • 脫離了之前魔鬼式訓(xùn)練的節(jié)奏后,生活狀態(tài)一下子變得輕松下來,思維的放松致使身體開始憊倦乞旦,對(duì)所有事都很難提起勁兒贼穆,不知...
    封筆ing閱讀 141評(píng)論 0 0
  • 即使在戰(zhàn)績(jī)很爛的近幾個(gè)賽季,從科比退役直到現(xiàn)在自媒體時(shí)代下球哥的粉墨登場(chǎng)兰粉,洛杉磯第一球隊(duì)始終承包著聯(lián)盟大部分...
    魔道人DoiMoi閱讀 382評(píng)論 0 1
  • 今天扮惦,明媚的陽(yáng)光令人心情愉快,而我卻有點(diǎn)小不開心亲桦。 生病的資味可不好受崖蜜,雖然只是有點(diǎn)小感冒。
    袁_dea閱讀 171評(píng)論 0 0