并發(fā)編程之線程第二篇

并發(fā)編程之線程第二篇

3.12 五種狀態(tài)

這是從操作系統(tǒng)層面來描述的


在這里插入圖片描述
  • 【初始狀態(tài)】僅是在語音層面創(chuàng)建了線程對象介袜,還未與操作系統(tǒng)線程關(guān)聯(lián)
  • 【可運行狀態(tài)】(就緒狀態(tài))指該線程已經(jīng)被創(chuàng)建(與操作系統(tǒng)線程關(guān)聯(lián))浇辜,可以由CPU調(diào)度執(zhí)行
  • 【運行狀態(tài)】指獲取了CPU時間片運行中的狀態(tài)
    (1)當(dāng)CPU時間片用完诞外,會從【運行狀態(tài)】轉(zhuǎn)換至【可運行狀態(tài)】慢洋,會導(dǎo)致線程的上下文切換
  • 【阻塞狀態(tài)】
    (1)如果調(diào)用了阻塞API,如BIO讀寫文件赖临,這時該線程實際不會用到CPU,會導(dǎo)致線程上下文切換,進入【阻塞狀態(tài)】
    (2)等BIO操作完畢卒稳,會由操作系統(tǒng)喚醒阻塞的線程,轉(zhuǎn)換至【可運行狀態(tài)】
    (3)與【可運行狀態(tài)】的區(qū)別是他巨,對【阻塞狀態(tài)】的線程來說只要它們一直不喚醒充坑,調(diào)度器就一直不會考慮調(diào)度它們
  • 【終止?fàn)顟B(tài)】表示線程已經(jīng)執(zhí)行完畢,生命周期已經(jīng)結(jié)束染突,不會再轉(zhuǎn)換為其它狀態(tài)

3.13 六種狀態(tài)

這是從Java API層面來描述的
根據(jù)Thread.State枚舉捻爷,分為六種狀態(tài)


在這里插入圖片描述
  • NEW 線程剛被創(chuàng)建,但是還沒有調(diào)用start()方法
  • RUNNABLE 當(dāng)調(diào)用了start()方法之后份企,注意也榄,Java API層面的RUNNABLE狀態(tài)涵蓋了操作系統(tǒng)層面的【可運行狀態(tài)】、【運行狀態(tài)】和【阻塞狀態(tài)】(由于BIO導(dǎo)致的線程阻塞司志,在Java里無法區(qū)分甜紫,任然認為是可運行)
  • BLOCKED、WAITING骂远、TIMED_WAITING都是Java API層面對【阻塞狀態(tài)】的細分囚霸。
  • TERMINATED當(dāng)線程代碼運行結(jié)束

4.1 共享帶來的問題

Java的體現(xiàn)
兩個線程對初始值為0的靜態(tài)變量一個做自增,一個做自減激才,各做5000次拓型,結(jié)果是0嗎?

在這里插入圖片描述

問題分析
y以上的結(jié)果可能是正數(shù)瘸恼、負數(shù)劣挫、零。為什么呢钞脂?因為Java中對靜態(tài)變量的自增揣云,自減并不是原子操作,要徹底理解冰啃,必須從字節(jié)碼來進行分析
例如對于 i++而言(i為靜態(tài)變量)邓夕,實際會產(chǎn)生如下的JVM字節(jié)碼指令 :
getstatic i // 獲取靜態(tài)變量i的值
iconst_1 // 準(zhǔn)備常量1
iadd // 自增
putstatic i // 將修改后的值存入靜態(tài)變量i
而對應(yīng) i-- 也是類似
getstatic i // 獲取靜態(tài)變量i的值
iconst_1 // 準(zhǔn)備常量1
isub // 自減
putstatic i // 將修改后的值存入靜態(tài)變量i
而Java的內(nèi)存模型如下,完成靜態(tài)變量的自增阎毅,自減需要在主存和工作內(nèi)存中進行數(shù)據(jù)交換 :

在這里插入圖片描述

如果是單線程以上8行代碼是順序執(zhí)行(不會交錯)沒有問題 :


在這里插入圖片描述

但多線程下這8行代碼可能交錯運行 :
出現(xiàn)負數(shù)的情況 :


在這里插入圖片描述

出現(xiàn)正數(shù)的情況


在這里插入圖片描述

臨界區(qū)Critical Section

  • 一個程序運行多個線程本身是沒有問題的

  • 問題出在多個線程訪問共享資源

    • 多個線程讀共享資源其實也沒有問題
    • 在多個線程對共享資源讀寫操作時發(fā)生指令交錯焚刚,就會出現(xiàn)問題
  • 一段代碼塊如果存在對共享資源的多線程讀寫操作,稱這段代碼塊為臨界區(qū)
    例如扇调,下面代碼中臨界區(qū)

    在這里插入圖片描述

    競態(tài)條件 Race Condition
    多個線程在臨界區(qū)內(nèi)執(zhí)行矿咕,由于代碼的執(zhí)行序列不同而導(dǎo)致結(jié)果無法預(yù)測,稱之為發(fā)生了競態(tài)條件

4.2 synchronized解決方案

  • 應(yīng)用之互斥
    為了避免臨界區(qū)的競態(tài)條件發(fā)生,有多種手段可以達到目的碳柱。

  • 阻塞式的解決方案 : synchronized,Lock

  • 非阻塞式的解決方案 : 原子變量
    本次使用阻塞式的解決方案 : synchronized捡絮,來解決上述問題尸疆,即俗稱的【對象鎖】衰猛,它采用互斥的方式讓同一個時刻最多只有一個線程能持有【對象鎖】,其它線程再想獲取這個【對象鎖】時就會阻塞住途茫。這樣就能保證擁有鎖的線程可以安全的執(zhí)行臨界區(qū)內(nèi)的代碼瑞侮,不用擔(dān)心線程上下文切換
    注意
    雖然java中互斥和同步都可以采用synchronized關(guān)鍵字來完成的圆,但它們還是有區(qū)別的 :

  • 互斥是保證臨界區(qū)的競態(tài)條件發(fā)生,同一時刻只能有一個線程執(zhí)行臨界區(qū)代碼

  • 同步是由于線程執(zhí)行的先后半火、順序不同越妈、需要一個線程等待其它線程運行到某個點
    synchronized
    語法

    在這里插入圖片描述

    解決


    在這里插入圖片描述
    在這里插入圖片描述

    所謂的線程八鎖
    其實就是考察synchronized鎖住的是哪個對象
    情況1 :

在這里插入圖片描述

用圖來解釋


在這里插入圖片描述
在這里插入圖片描述

思考
synchronized實際是用對象鎖保證了臨界區(qū)內(nèi)代碼的原子性,臨界區(qū)內(nèi)的代碼對外是不可分割的钮糖,不會被線程切換所打斷梅掠。
為了加深理解,請思考下面的問題

  • 如果把synchronized(obj)放在for循環(huán)的外面藐鹤,如何理解瓤檐?-- 原子性
  • 如果t1 synchronized(obj1) 而 t2 synchronized(obj2)會怎樣運作? – 鎖對象
  • 如果t1 synchronized(obj) 而t2 沒有加會怎么樣娱节?如何理解挠蛉? – 鎖對象
    面向?qū)ο蟾倪M
    把需要保護的共享變量放入一個類
    在這里插入圖片描述

4.4 變量的線程安全分析

成員變量和靜態(tài)變量是否線程安全?

  • 如果它們沒有共享肄满,則線程安全

  • 如果它們被共享了谴古,根據(jù)它們的狀態(tài)是否能夠改變,又分兩種情況

    • 如果只有讀操作稠歉,則線程安全
    • 如果有讀寫操作掰担,則這段代碼是臨界區(qū),需要考慮線程安全
      局部變量是否線程安全怒炸?
  • 局部變量是線程安全的

  • 但局部變量引用的對象則未必

    • 如果該對象沒有逃離方法的作用訪問带饱,它是線程安全的

    • 如果該對象逃離方法的作用范圍,需要考慮線程安全
      局部變量線程安全分析

      在這里插入圖片描述

      每個線程調(diào)用test1()方法時局部變量i阅羹,會在每個線程的棧幀內(nèi)存中被創(chuàng)建多份勺疼,因此不存在共享


      在這里插入圖片描述

      如圖


      在這里插入圖片描述

      局部變量的引用稍有不同
      先看一個成員變量的例子


      在這里插入圖片描述
      在這里插入圖片描述

      其中一種情況是,如果線程2還未add捏鱼,線程1remove就會報錯 :


      在這里插入圖片描述

      分析 :

  • 無論哪個線程中的method2引用的都是同一個對象中的list成員變量

  • method3與method2分析相同


    在這里插入圖片描述

    將list修改成局部變量


    在這里插入圖片描述

    分析 :

  • list是局部變量执庐,每個線程調(diào)用時會創(chuàng)建其不同實例,沒有共享

  • 而method2的蠶食是從method1中傳遞過來的导梆,與method1中引用同一個對象

  • method3的參數(shù)分析與method2相同


    在這里插入圖片描述

    方法訪問修飾符帶來的思考轨淌,如果把method2和method3的方法修改為public會不會代理線程安全問題迂烁?

  • 情況1 :有其它線程調(diào)用method2和method3

  • 情況2 :在情況1的基礎(chǔ)上,為ThreadSafe類添加子類递鹉,子類覆蓋method2或method3方法盟步。
    [圖片上傳失敗...(image-6b1671-1581171046271)]

    從這里例子中可以看出private或final提高【安全】的意義所在,請體會開閉原則中的【閉】
    常見線程安全類

  • String

  • Integer

  • StringBuffer

  • Random

  • Vector

  • HashTable

  • java.util.concurrent包下的類
    這里說它們是線程安全的是指躏结,多個線程調(diào)用它們同一個實例的某個方法時址芯,是線程安全的。也可以理解為

  • 它們的每個方法是原子的

  • 但注意它們多個方法的組合不是原子的窜觉,見后面分析
    線程安全類方法的組合
    分析下面代碼是否線程安全?


    在這里插入圖片描述
    在這里插入圖片描述

    不可變類線程安全性
    String北专、Integer等都是不可變類禀挫,因為其內(nèi)部的狀態(tài)不可以改變,因此它們的方法都是線程安全的拓颓。

    在這里插入圖片描述

    在這里插入圖片描述

    其中foo的行為是不確定的语婴,可能導(dǎo)致不安全的發(fā)生,被稱之為外星方法

    在這里插入圖片描述

    請比較JDK中String類的實現(xiàn)驶睦,為什么是final修飾的砰左?因為防止子類去實現(xiàn),這樣會引起線程安全問題场航。也是符合開閉原則

4.6 Monitor概念

Java對象頭
以32位虛擬機為例
普通對象

在這里插入圖片描述

數(shù)組對象


在這里插入圖片描述

其中Mark Word結(jié)構(gòu)為


在這里插入圖片描述

Monitor
Monitor被翻譯為監(jiān)視器管程
每個Java對象都可以關(guān)聯(lián)一個Monitor對象缠导,如果使用synchronized給對象上鎖(重量級)之后,該對象頭的Mark Word中就被設(shè)置指向Monitor對象的指針
Monitor結(jié)構(gòu)如下

在這里插入圖片描述

  • 剛開始Monitor中Owner為null
  • 當(dāng)Thread-2指向synchronized(obj)就會將Monitor的所有者Owner置為Thread-2溉痢,Monitor中只能有一個Owner
  • 在Thread-2上鎖的過程中僻造,如果Thread-3,Thread-4孩饼,Thread-5也來執(zhí)行synchronized(obj)髓削,就會進入EntryList BLOCKED
  • Thread-2執(zhí)行完同步代碼塊的內(nèi)容,然后喚醒EntryList中等待的線程來競爭鎖镀娶,競爭的時是非公平的
  • 圖中WaitSet中的Thread-0立膛,Thread-1是之前獲得過鎖,但條件不滿足進入WAITING狀態(tài)的線程梯码,后面講wait-notify時會分析
    注意 :
    • synchronized必須是進入同一個對象的monitor才有上述的效果

    • 不加synchronized的對象不會關(guān)聯(lián)監(jiān)視器宝泵,不遵從以上規(guī)則
      原理之synchronized

      在這里插入圖片描述

      對應(yīng)的字節(jié)碼


      在這里插入圖片描述

1. 輕量級鎖

輕量級鎖的使用場景 :如果一個對象雖然有多線程訪問,但多線程訪問的時間是錯開的(也就是沒有競爭)忍些,那么可以使用輕量級鎖來優(yōu)化
輕量級鎖來優(yōu)化鲁猩。
輕量級鎖對使用者是透明的,即語法任然是synchronized
假設(shè)有兩個方法同步塊罢坝,利用同一個對象加鎖


在這里插入圖片描述
  • 創(chuàng)建鎖記錄(Lock Record)對象廓握,每個線程都的棧幀都會包含一個鎖記錄的結(jié)構(gòu)搅窿,內(nèi)部可以存儲鎖定對象的Mark Word


    在這里插入圖片描述
  • 讓鎖記錄中Object reference指向鎖對象,并嘗試用cas替換Object的Mark Word隙券,將Mark Word的值存入鎖記錄


    在這里插入圖片描述
  • 如果cas替換成功男应,對象頭中存儲了鎖記錄地址和狀態(tài) 00,表示由該線程給對象加鎖娱仔,這時圖示如下


    在這里插入圖片描述
  • 如果cas失敗沐飘,有兩種情況

    • 如果是其他線程已經(jīng)持有了該Object的輕量級鎖,這時表面有競爭牲迫,進入膨脹過程
    • 如果是自己執(zhí)行了synchronized鎖重入耐朴,那么再添加一條Lock Record作為重入的計數(shù)


      在這里插入圖片描述
  • 當(dāng)退出synchronized代碼塊(解鎖時)如果有取值為null的鎖記錄,表示有重入盹憎,這時重置鎖記錄筛峭,表示重入計數(shù)減一


    在這里插入圖片描述
  • 當(dāng)退出synchronized代碼塊(解鎖時)鎖記錄的值不為null,這時使用cas將Mark Word的值恢復(fù)給對象頭

    • 成功陪每,則解鎖成功
    • 失敗影晓,說明輕量級鎖進行了鎖膨脹或已經(jīng)升級為重量級鎖,進入重量級鎖解鎖流程

2. 鎖膨脹

如果在嘗試加輕量級鎖的過程中檩禾,CAS操作無法成功挂签,這時一種情況就是有其他線程為此對象加上了輕量級鎖(有競爭),這時需要進行鎖膨脹盼产,將輕量級鎖變?yōu)橹亓考夋i饵婆。


在這里插入圖片描述
  • 當(dāng)Thread-1進行輕量級加鎖時,Thread-0已結(jié)對該對象加了輕量級鎖


    在這里插入圖片描述
  • 這時Thread-1加輕量級鎖失敗戏售,進入鎖膨脹流程

    • 即為Object對象申請Monitor鎖啦辐,讓Object指向重量級鎖地址
    • 然后自己進入Monitor的EntryList BLOCKED


      在這里插入圖片描述
  • 當(dāng)Thread-0退出同步塊解鎖時,使用cas將Mark Word的值恢復(fù)給對象頭蜈项,失敗芹关。這時會進入重量級解鎖流程,即 按照Monitor地址找到Monitor對象紧卒,設(shè)置Owner為null侥衬,喚醒EntryList中BLOCKED線程。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末跑芳,一起剝皮案震驚了整個濱河市轴总,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌博个,老刑警劉巖怀樟,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異盆佣,居然都是意外死亡往堡,警方通過查閱死者的電腦和手機械荷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來虑灰,“玉大人吨瞎,你說我怎么就攤上這事∧赂溃” “怎么了颤诀?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長对湃。 經(jīng)常有香客問我崖叫,道長,這世上最難降的妖魔是什么拍柒? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任归露,我火速辦了婚禮,結(jié)果婚禮上斤儿,老公的妹妹穿的比我還像新娘。我一直安慰自己恐锦,他們只是感情好往果,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著一铅,像睡著了一般陕贮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上潘飘,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天肮之,我揣著相機與錄音,去河邊找鬼卜录。 笑死戈擒,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的艰毒。 我是一名探鬼主播筐高,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼丑瞧!你這毒婦竟也來了柑土?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤绊汹,失蹤者是張志新(化名)和其女友劉穎稽屏,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體西乖,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡狐榔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年坛增,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荒叼。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡轿偎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出被廓,到底是詐尸還是另有隱情坏晦,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布嫁乘,位于F島的核電站昆婿,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蜓斧。R本人自食惡果不足惜仓蛆,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望挎春。 院中可真熱鬧,春花似錦直奋、人聲如沸能庆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽搁胆。三九已至,卻和暖如春邮绿,著一層夾襖步出監(jiān)牢的瞬間渠旁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工船逮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留顾腊,地道東北人。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓挖胃,卻偏偏與公主長得像投慈,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子冠骄,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354

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