線程安全

線程安全定義

《Java Concurrency In Practice》一書中定義“線程安全”:當(dāng)多個線程訪問一個對象時(shí)奥此,如果不用考慮這些線程在運(yùn)行時(shí)環(huán)境下的調(diào)度和交替執(zhí)行虐拓,也不需要進(jìn)行額外的同步嘁捷,或者在調(diào)用方進(jìn)行任何其他的協(xié)調(diào)操作厦画,調(diào)用這個對象的行為都可以獲得正確的結(jié)果,那這個對象是線程安全的翘紊。
這個定義比較嚴(yán)謹(jǐn)换薄,它要求線程安全的代碼都必須具備一個特征:代碼本身封裝了所有必要的正確性保障手段(如互斥同步等),令調(diào)用者無須關(guān)心多線程的問題融师,更無須自己采取任何措施來保證多線程的正確調(diào)用右钾。
Java語言中線程安全中,各種操作共享的數(shù)據(jù)分為以下5類:不可變旱爆、絕對線程安全舀射、相對線程安全、線程兼容和線程對立怀伦。

不可變

不可變的對象一定是線程安全的脆烟,不論對象的方法實(shí)現(xiàn)還是方法的調(diào)用者,都不需要采取任何線程安全保障措施房待⌒细幔“不可變”帶來的安全性是最簡單和最純粹的。
在Java語言中桑孩,如果共享數(shù)據(jù)是一個基本數(shù)據(jù)類型拜鹤,只要final關(guān)鍵字修飾就可以保證它是不可變的。如果共享對象是一個對象流椒,那就需要保證對象的行為不會對其狀態(tài)產(chǎn)生任何影響才行敏簿。

絕對線程安全

完全滿足上訴線程安全的定義,這個定義其實(shí)是很嚴(yán)格的,通常需要付出很大的代價(jià)才能達(dá)到惯裕,甚至有時(shí)候是不切實(shí)際的代價(jià)温数。

相對線程安全

相對的線程安全就是我們通常意義上所講的線程安全,它需要保證對這個對象單獨(dú)的操作是線程安全的蜻势。我們在調(diào)用的時(shí)候不需要做額外的保障措施撑刺,但是對于一些特定順序的連續(xù)調(diào)用,就可能需要在調(diào)用端使用額外的同步手段來保證調(diào)用的正確性握玛。在Java語言中猜煮,大部分的線程安全類都屬于這種類型,例如Vector败许、HashTable王带、Collections的synchronizedCollection()方法包裝的集合等。

線程兼容

線程兼容是指對象本身并不是線程安全的市殷,但是可以通過在調(diào)用端正確地使用同步手段來保證對象在并發(fā)環(huán)境中可以安全地使用愕撰,我們平常說一個類不是線程安全的,絕大多數(shù)時(shí)候指的是這種情況醋寝。Java API中大部分的類都是屬于線程兼容的搞挣,如ArrayList和HashMap等。

線程對立

線程對立指無論調(diào)用端是否采取了同步措施音羞,都無法在多線程環(huán)境中并發(fā)使用的代碼囱桨。由于Java語言天生就具備多線程特性,線程對立這種排斥多線程的代碼是很少出現(xiàn)嗅绰,而且通常都是有害的舍肠,應(yīng)當(dāng)避免。常見的例子有Thread類的suspend()和resume()方法窘面,System.setIn()翠语、System.setOut()和System.runFinalizersOnExit()等。

線程安全的實(shí)現(xiàn)

互斥同步

互斥同步(Mutual Exclusion & Synchronion)是常見的一種并發(fā)正確性保障手段财边。同步是指在多個線程并發(fā)訪問共享數(shù)據(jù)時(shí)肌括,保證共享數(shù)據(jù)在同一時(shí)刻只被一個(或者是一些,使用信號量的時(shí)候)線程使用酣难。而互斥是實(shí)現(xiàn)同步的一種手段谍夭,臨界區(qū)(Critical Section)、互斥量(Mutex)和信號量(Semaphore)都是主要的互斥實(shí)現(xiàn)方式憨募。因此這里面紧索,互斥是因,同步是果馋嗜,互斥是方法齐板,同步是目的。
在Java中葛菇,最基本的互斥同步方法就是synchronized關(guān)鍵字甘磨,synchronized關(guān)鍵字經(jīng)過編譯之后,會在同步塊的前后分別形成monitorenter和monitorexit這兩個字節(jié)碼指令眯停,這兩個字節(jié)碼都需要一個reference類型的參數(shù)來明確要鎖定和解鎖的對象济舆。synchronized同步塊對同一線程來說是可重入的 ,不會出現(xiàn)自己把自己鎖死的情況莺债,其次滋觉,同步塊在已進(jìn)入線程執(zhí)行完之前,會阻塞后面其他線程的進(jìn)入齐邦。
除了使用synchronized之外椎侠,我們還可以使用java.util.concurrent包中的重入鎖(ReentrantLock)來實(shí)現(xiàn)同步。相比于synchronized措拇,ReentrantLock增加了一些高級功能:

  • 等待可中斷是指當(dāng)持有鎖的線程長期不釋放鎖的時(shí)候我纪,正在等待的線程可以選擇放棄等待,改為處理其他事情丐吓,可中斷特性對處理執(zhí)行非常長的同步塊很有幫助
  • 公平鎖是指多個線程在等待同一個鎖時(shí)浅悉,必須按照申請鎖的時(shí)間順序來依次獲得鎖;而非公平鎖則不保證這一點(diǎn)券犁,在鎖被釋放時(shí)术健,任何一個等待鎖的線程都有機(jī)會獲得鎖。synchronized是非公平鎖粘衬,ReentrantLock默認(rèn)情況下是非公平的荞估,但是可以設(shè)置成公平鎖
  • 鎖綁定多個條件是指一個ReentrantLock對象可以同時(shí)綁定多個Condition對象,而synchronized中稚新,鎖對象的wait()和notify()或者notifyAll()方法可以實(shí)現(xiàn)一個隱含的條件泼舱,如果要和多于一個條件關(guān)聯(lián)的時(shí)候,就不得不額外地添加一個鎖枷莉,而ReentrantLock則無須這樣做娇昙,只需要多次調(diào)用newCondition()方法即可。

非阻塞同步

互斥同步最主要的問題就是進(jìn)行線程阻塞和喚醒所帶來的性能問題笤妙,因此這種同步也稱為阻塞同步(Blocking Synchronization)冒掌。從出了問題的方式來說,互斥同步是一種悲觀的并發(fā)策略蹲盘,總是認(rèn)為要是不做正確的同步措施股毫,就肯定出現(xiàn)問題。隨著硬件指令集的發(fā)展召衔,可以采用基于沖突檢測的樂觀并發(fā)策略铃诬,通俗的將就是先進(jìn)行操作,產(chǎn)生了沖突,那就再采取其他補(bǔ)償措施趣席,這種樂觀的并發(fā)策略許多實(shí)現(xiàn)都不需要把線程掛起兵志,因此這種同步操作稱為非阻塞同步(Non-Blockinig Synchronization)。

無同步方案

要保證線程安全宣肚,并不是一定要進(jìn)行同步想罕,兩者沒有因果關(guān)系。同步只是保證共享數(shù)據(jù)爭用時(shí)的正確性的手段霉涨,如果一個方法本來就不涉及共享數(shù)據(jù)按价,那它自然就無須任何同步措施去保證正確性,因此會有一些代碼天生就是線程安全的笙瑟。
可重入代碼:這種代碼也叫做純代碼(Pure Code)楼镐,可以在代碼執(zhí)行的任何時(shí)刻中斷它,轉(zhuǎn)而去執(zhí)行另外一段代碼(包括遞歸調(diào)用本身)往枷,而在控制權(quán)返回后框产,原來的程序不會出現(xiàn)任何錯誤。相對線程安全來說师溅,可重入性是更基本的特性茅信,它可以保證線程安全,即所有的可重入代碼都是線程安全的墓臭,但是并不是所有的線程安全代碼都是可重入的蘸鲸。可重入代碼有一些共同特征窿锉,例如不依賴存儲在堆上的數(shù)據(jù)和公用的系統(tǒng)資源酌摇,用到的狀態(tài)量都由參數(shù)中傳入、不調(diào)用非可重入的方法等嗡载。
線程本地存儲(Thread Local Storage):如果一段代碼中所需要的數(shù)據(jù)必須與其他代碼共享窑多,那就看看這些共享數(shù)據(jù)是否能保證在同一個線程中執(zhí)行,如果能保證洼滚,就可以把共享數(shù)據(jù)的可見范圍限制在同一線程之內(nèi)埂息,這樣無須同步也能保證下城之間不出現(xiàn)數(shù)據(jù)爭用的問題。
符合這種特點(diǎn)的應(yīng)用并不少遥巴,典型的就是消費(fèi)隊(duì)列架構(gòu)模式千康,都會將產(chǎn)品的消費(fèi)過程盡量在一個線程中消費(fèi)完。Java語言中铲掐,如果一個變量要被多線程訪問拾弃,可以使用volatile關(guān)鍵字聲明為“易變的”:如果一個變量要被某個線程獨(dú)享,可以通過java.lang.ThreadLocal類來實(shí)現(xiàn)線程本地存儲的功能摆霉。每個線程的Thread對象都有一個ThreadLocalMap對象豪椿,這個對象存儲了一組以ThreadLocal.threadLocalHashCode為鍵奔坟,以本地線程變量為值的K-V值對,ThreadLocal對象就是當(dāng)前線程的ThreadLocalMap的訪問入口搭盾,每個ThreadLocal對象都包含了獨(dú)一無二的threadLocalHashCode值咳秉,使用這個值就可以在線程K-V值對中找回對應(yīng)的本地線程變量。

參考資料

  • 深入理解Java虛擬機(jī) JVM高級特性與最佳實(shí)踐 第2版
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末增蹭,一起剝皮案震驚了整個濱河市滴某,隨后出現(xiàn)的幾起案子磅摹,更是在濱河造成了極大的恐慌滋迈,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件户誓,死亡現(xiàn)場離奇詭異饼灿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)帝美,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門碍彭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人悼潭,你說我怎么就攤上這事庇忌。” “怎么了舰褪?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵皆疹,是天一觀的道長。 經(jīng)常有香客問我占拍,道長略就,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任晃酒,我火速辦了婚禮表牢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘贝次。我一直安慰自己崔兴,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布蛔翅。 她就那樣靜靜地躺著敲茄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪搁宾。 梳的紋絲不亂的頭發(fā)上折汞,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天,我揣著相機(jī)與錄音盖腿,去河邊找鬼爽待。 笑死损同,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的鸟款。 我是一名探鬼主播膏燃,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼何什!你這毒婦竟也來了组哩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤处渣,失蹤者是張志新(化名)和其女友劉穎伶贰,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體罐栈,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡黍衙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年旧蛾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了誓焦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡晒杈,死狀恐怖柑贞,靈堂內(nèi)的尸體忽然破棺而出方椎,到底是詐尸還是另有隱情,我是刑警寧澤钧嘶,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布棠众,位于F島的核電站,受9級特大地震影響康辑,放射性物質(zhì)發(fā)生泄漏摄欲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一疮薇、第九天 我趴在偏房一處隱蔽的房頂上張望胸墙。 院中可真熱鬧,春花似錦按咒、人聲如沸迟隅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽智袭。三九已至,卻和暖如春掠抬,著一層夾襖步出監(jiān)牢的瞬間吼野,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工两波, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瞳步,地道東北人闷哆。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像单起,于是被迫代替她去往敵國和親抱怔。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評論 2 359

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