java線程相關(guān)整理

一精耐、實(shí)現(xiàn)方式

線程的實(shí)現(xiàn)有兩種方式,一是繼承Thread類克饶,二是實(shí)現(xiàn)Runnable接口酝蜒。

二、狀態(tài)

1.新建狀態(tài)

創(chuàng)建了一個(gè)線程對象矾湃。

2.就緒狀態(tài)

線程對象創(chuàng)建后亡脑,其他線程調(diào)用了該對象的start()方法。該狀態(tài)的線程位于可運(yùn)行線程池中邀跃,變得可運(yùn)行霉咨,等待獲取CPU的使用權(quán)。

3.運(yùn)行狀態(tài)

就緒狀態(tài)的線程獲取了CPU拍屑,執(zhí)行run()方法躯护。

4.阻塞狀態(tài)

阻塞狀態(tài)是線程因?yàn)槟撤N原因放棄CPU使用權(quán),暫時(shí)停止運(yùn)行丽涩。直到線程進(jìn)入就緒狀態(tài)棺滞,才有機(jī)會轉(zhuǎn)到運(yùn)行狀態(tài)。阻塞的情況分三種:

a.等待阻塞

運(yùn)行的線程執(zhí)行wait()方法矢渊,JVM會把該線程放入等待池中继准。

b.同步阻塞

運(yùn)行的線程在獲取對象的同步鎖時(shí),若該同步鎖被別的線程占用矮男,則JVM會把該線程放入鎖池中移必。

c.其他阻塞

運(yùn)行的線程執(zhí)行sleep()或join()方法,或者發(fā)出了I/O請求時(shí)毡鉴,JVM會把該線程置為阻塞狀態(tài)崔泵。當(dāng)sleep()狀態(tài)超時(shí)、join()等待線程終止或者超時(shí)猪瞬、或者I/O處理完畢時(shí)憎瘸,線程重新轉(zhuǎn)入就緒狀態(tài)。

5.死亡狀態(tài)

線程執(zhí)行完了或者因異常退出了run()方法陈瘦,該線程結(jié)束生命周期幌甘。

三、run和start的區(qū)別

當(dāng)調(diào)用start方法的時(shí)候痊项,該線程就進(jìn)入就緒狀態(tài)锅风。等待CPU進(jìn)行調(diào)度執(zhí)行,此時(shí)還沒有真正執(zhí)行線程鞍泉。
當(dāng)調(diào)用run方法的時(shí)候皱埠,是已經(jīng)被CPU進(jìn)行調(diào)度,執(zhí)行線程的主要任務(wù)咖驮。

四边器、wait泪姨,join,sleep饰抒,yield, notify,notifyall诀黍,synchronized相關(guān)釋義

1.wait

在其他線程調(diào)用對象的notify或notifyAll方法前袋坑,導(dǎo)致當(dāng)前線程等待。線程會釋放掉它所占有的“鎖標(biāo)志”眯勾,從而使別的線程有機(jī)會搶占該鎖枣宫。
喚醒當(dāng)前對象鎖的等待線程使用notify或notifyAll方法,wait() 和notify()必須在synchronized函數(shù)或synchronized block中進(jìn)行調(diào)用。

2.sleep

在指定時(shí)間內(nèi)讓當(dāng)前正在執(zhí)行的線程暫停執(zhí)行吃环,但不會釋放“鎖標(biāo)志”也颤。不推薦使用。sleep()使當(dāng)前線程進(jìn)入阻塞狀態(tài)郁轻,在指定時(shí)間內(nèi)不會執(zhí)行翅娶。

3.yield

暫停當(dāng)前正在執(zhí)行的線程對象。yield()只是使當(dāng)前線程重新回到可執(zhí)行狀態(tài)好唯,所以執(zhí)行yield()的線程有可能在進(jìn)入到可執(zhí)行狀態(tài)后馬上又被執(zhí)行竭沫。yield()只能使同優(yōu)先級或更高優(yōu)先級的線程有執(zhí)行的機(jī)會。

4.join

等待該線程終止骑篙。等待調(diào)用join方法的線程結(jié)束蜕提,再繼續(xù)執(zhí)行。如:在main線程中執(zhí)行t.join()靶端,主要用于等待t線程運(yùn)行結(jié)束谎势,若無此句,main則會執(zhí)行完畢杨名,導(dǎo)致結(jié)果不可預(yù)測脏榆。(join方法會釋放this對象的鎖)

五、sleep() 與 wait()的區(qū)別

1.有關(guān)類

這兩個(gè)方法來自不同的類分別是台谍,sleep來自Thread類姐霍,和wait來自O(shè)bject 類。

2.有關(guān)鎖

最主要是sleep方法沒有釋放鎖典唇,而wait方法釋放了鎖镊折,使得其他線程可以使用同步控制塊或者方法。

3.有關(guān)資源

sleep不出讓系統(tǒng)資源介衔;wait是進(jìn)入線程等待池等待恨胚,出讓系統(tǒng)資源,其他線程可以占用CPU炎咖。一般wait不會加時(shí)間限制赃泡,因?yàn)槿绻鹷ait線程的運(yùn)行資源不夠寒波,再出來也沒用,要等待其他線程調(diào)用notify/notifyAll喚醒等待池中的所有線程升熊,才會進(jìn)入就緒隊(duì)列等待OS分配系統(tǒng)資源俄烁。

4.有關(guān)喚醒

sleep(milliseconds)可以用時(shí)間指定使它自動喚醒過來,如果時(shí)間不到只能調(diào)用interrupt()強(qiáng)行打斷级野。

5.有關(guān)使用

wait页屠,notify和notifyAll只能在同步控制方法或者同步控制塊里面使用,而sleep可以在任何地方使用

6.有關(guān)異常

sleep需要捕獲異常,而wait不需要

六蓖柔、ThreadLocal

1.定義

ThreadLocal 提供了線程本地的實(shí)例辰企。它與普通變量的區(qū)別在于,每個(gè)使用該變量的線程都會初始化一個(gè)完全獨(dú)立的實(shí)例副本况鸣。ThreadLocal 變量通常被private static修飾牢贸。當(dāng)一個(gè)線程結(jié)束時(shí),它所使用的所有 ThreadLocal 相對的實(shí)例副本都可被回收镐捧。

2.適用場景和總結(jié)

  • 每個(gè)線程需要有自己單獨(dú)的實(shí)例
  • 實(shí)例需要在多個(gè)方法中共享潜索,但不希望被多線程共享
  • ThreadLocal 并不解決線程間共享數(shù)據(jù)的問題
  • ThreadLocal 通過隱式的在不同線程內(nèi)創(chuàng)建獨(dú)立實(shí)例副本避免了實(shí)例線程安全的問題
  • 每個(gè)線程持有一個(gè) Map 并維護(hù)了 ThreadLocal 對象與具體實(shí)例的映射,該 Map 由于只被持有它的線程訪問懂酱,故不存在線程安全以及鎖的問題
  • ThreadLocalMap 的 Entry 對 ThreadLocal 的引用為弱引用帮辟,避免了 ThreadLocal 對象無法被回收的問題
  • ThreadLocalMap 的 set 方法通過調(diào)用 replaceStaleEntry 方法回收鍵為 null 的 Entry 對象的值(即為具體實(shí)例)以及 Entry 對象本身從而防止內(nèi)存泄漏
  • ThreadLocal 適用于變量在線程間隔離且在方法間共享的場景

七、線程安全

1.概念

  • 原子性

一個(gè)操作(有可能包含有多個(gè)子操作)要么全部執(zhí)行(生效)玩焰,要么全部都不執(zhí)行(都不生效)由驹。

  • 可見性

當(dāng)多個(gè)線程并發(fā)訪問共享變量時(shí),一個(gè)線程對共享變量的修改昔园,其它線程能夠立即看到蔓榄。

  • 順序性

程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。

2.如何解決多線程并發(fā)問題

  • 如何保證原子性

a.鎖和同步
  • 常用的保證Java操作原子性的工具是鎖和同步方法(或者同步代碼塊)默刚。使用鎖甥郑,可以保證同一時(shí)間只有一個(gè)線程能拿到鎖,也就保證了同一時(shí)間只有一個(gè)線程能執(zhí)行申請鎖和釋放鎖之間的代碼荤西。
  • 與鎖類似的是同步方法或者同步代碼塊澜搅。使用非靜態(tài)同步方法時(shí),鎖住的是當(dāng)前實(shí)例邪锌;使用靜態(tài)同步方法時(shí)勉躺,鎖住的是該類的Class對象;使用靜態(tài)代碼塊時(shí)觅丰,鎖住的是synchronized關(guān)鍵字后面括號內(nèi)的對象饵溅。
  • 無論使用鎖還是synchronized,本質(zhì)都是一樣妇萄,通過鎖來實(shí)現(xiàn)資源的排它性蜕企,從而實(shí)際目標(biāo)代碼段同一時(shí)間只會被一個(gè)線程執(zhí)行咬荷,進(jìn)而保證了目標(biāo)代碼段的原子性。這是一種以犧牲性能為代價(jià)的方法轻掩。
b.CAS(compare and swap)

基礎(chǔ)類型變量自增(i++)是一種常被誤以為是原子操作而實(shí)際不是的操作幸乒。Java中提供了對應(yīng)的原子操作類來實(shí)現(xiàn)該操作,并保證原子性唇牧,其本質(zhì)是利用了CPU級別的CAS指令罕扎。由于是CPU級別的指令,其開銷比需要操作系統(tǒng)參與的鎖的開銷小奋构。AtomicInteger使用方法如下。

AtomicInteger atomicInteger = new AtomicInteger();
for(int b = 0; b < numThreads; b++) {
  new Thread(() -> {
    for(int a = 0; a < iteration; a++) {
      atomicInteger.incrementAndGet();
    }
  }).start();
}
  • 如何保證可見性

Java提供了volatile關(guān)鍵字來保證可見性拱层。當(dāng)使用volatile修飾某個(gè)變量時(shí)弥臼,它會保證對該變量的修改會立即被更新到內(nèi)存中,并且將其它緩存中對該變量的緩存設(shè)置成無效根灯,因此其它線程需要讀取該值時(shí)必須從主內(nèi)存中讀取径缅,從而得到最新的值。

  • 如何保證順序性

  • Java中可通過volatile在一定程序上保證順序性烙肺,另外還可以通過synchronized和鎖來保證順序性纳猪。
  • synchronized和鎖保證順序性的原理和保證原子性一樣,都是通過保證同一時(shí)間只會有一個(gè)線程執(zhí)行目標(biāo)代碼段來實(shí)現(xiàn)的桃笙。
  • 除了從應(yīng)用層面保證目標(biāo)代碼段執(zhí)行的順序性外氏堤,JVM還通過被稱為happens-before原則隱式地保證順序性。兩個(gè)操作的執(zhí)行順序只要可以通過happens-before推導(dǎo)出來搏明,則JVM會保證其順序性鼠锈,反之JVM對其順序性不作任何保證,可對其進(jìn)行任意必要的重新排序以獲取高效率星著。

happens-before原則(先行發(fā)生原則)

  • 傳遞規(guī)則:如果操作1在操作2前面购笆,而操作2在操作3前面,則操作1肯定會在操作3前發(fā)生虚循。該規(guī)則說明了happens-before原則具有傳遞性
  • 鎖定規(guī)則:一個(gè)unlock操作肯定會在后面對同一個(gè)鎖的lock操作前發(fā)生同欠。這個(gè)很好理解,鎖只有被釋放了才會被再次獲取
  • volatile變量規(guī)則:對一個(gè)被volatile修飾的寫操作先發(fā)生于后面對該變量的讀操作
  • 程序次序規(guī)則:一個(gè)線程內(nèi)横缔,按照代碼順序執(zhí)行
  • 線程啟動規(guī)則:Thread對象的start()方法先發(fā)生于此線程的其它動作
  • 線程終結(jié)原則:線程的終止檢測后發(fā)生于線程中其它的所有操作
  • 線程中斷規(guī)則: 對線程interrupt()方法的調(diào)用先發(fā)生于對該中斷異常的獲取
  • 對象終結(jié)規(guī)則:一個(gè)對象構(gòu)造先于它的finalize發(fā)生

3.volatile適用場景

volatile適用于不需要保證原子性铺遂,但卻需要保證可見性的場景。一種典型的使用場景是用它修飾用于停止線程的狀態(tài)標(biāo)記茎刚。如下所示

boolean isRunning = false;

public void start () {
  new Thread( () -> {
    while(isRunning) {
      someOperation();
    }
  }).start();
}

public void stop () {
  isRunning = false;
}

在這種實(shí)現(xiàn)方式下娃循,即使其它線程通過調(diào)用stop()方法將isRunning設(shè)置為false,循環(huán)也不一定會立即結(jié)束斗蒋“聘可以通過volatile關(guān)鍵字笛质,保證while循環(huán)及時(shí)得到isRunning最新的狀態(tài)從而及時(shí)停止循環(huán),結(jié)束線程捞蚂。

4.相關(guān)問題

問:平時(shí)項(xiàng)目中使用鎖和synchronized比較多妇押,而很少使用volatile,難道就沒有保證可見性姓迅?
答:鎖和synchronized即可以保證原子性敲霍,也可以保證可見性。都是通過保證同一時(shí)間只有一個(gè)線程執(zhí)行目標(biāo)代碼段來實(shí)現(xiàn)的丁存。

問:鎖和synchronized為何能保證可見性肩杈?
答:根據(jù)JDK 7的Java doc中對concurrent包的說明,一個(gè)線程的寫結(jié)果保證對另外線程的讀操作可見解寝,只要該寫操作可以由happen-before原則推斷出在讀操作之前發(fā)生扩然。

The results of a write by one thread are guaranteed to be visible to a read by another thread only if the write operation happens-before the read operation. The synchronized and volatile constructs, as well as the Thread.start() and Thread.join() methods, can form happens-before relationships.

問:既然鎖和synchronized即可保證原子性也可保證可見性,為何還需要volatile聋伦?
答:synchronized和鎖需要通過操作系統(tǒng)來仲裁誰獲得鎖夫偶,開銷比較高,而volatile開銷小很多觉增。因此在只需要保證可見性的條件下兵拢,使用volatile的性能要比使用鎖和synchronized高得多。

問:既然鎖和synchronized可以保證原子性逾礁,為什么還需要AtomicInteger這種的類來保證原子操作说铃?
答:鎖和synchronized需要通過操作系統(tǒng)來仲裁誰獲得鎖,開銷比較高嘹履,而AtomicInteger是通過CPU級的CAS操作來保證原子性截汪,開銷比較小。所以使用AtomicInteger的目的還是為了提高性能植捎。

問:還有沒有別的辦法保證線程安全
答:有衙解。盡可能避免引起非線程安全的條件——共享變量。如果能從設(shè)計(jì)上避免共享變量的使用焰枢,即可避免非線程安全的發(fā)生蚓峦,也就無須通過鎖或者synchronized以及volatile解決原子性、可見性和順序性的問題济锄。

問:synchronized即可修飾非靜態(tài)方式暑椰,也可修飾靜態(tài)方法,還可修飾代碼塊荐绝,有何區(qū)別
答:synchronized修飾非靜態(tài)同步方法時(shí)一汽,鎖住的是當(dāng)前實(shí)例;synchronized修飾靜態(tài)同步方法時(shí),鎖住的是該類的Class對象召夹;synchronized修飾靜態(tài)代碼塊時(shí)岩喷,鎖住的是synchronized關(guān)鍵字后面括號內(nèi)的對象。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末监憎,一起剝皮案震驚了整個(gè)濱河市纱意,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鲸阔,老刑警劉巖偷霉,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異褐筛,居然都是意外死亡类少,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進(jìn)店門渔扎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來硫狞,“玉大人,你說我怎么就攤上這事赞警〖巳蹋” “怎么了虏两?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵愧旦,是天一觀的道長。 經(jīng)常有香客問我定罢,道長笤虫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任祖凫,我火速辦了婚禮琼蚯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘惠况。我一直安慰自己遭庶,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布稠屠。 她就那樣靜靜地躺著峦睡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪权埠。 梳的紋絲不亂的頭發(fā)上榨了,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天,我揣著相機(jī)與錄音攘蔽,去河邊找鬼龙屉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛满俗,可吹牛的內(nèi)容都是我干的转捕。 我是一名探鬼主播作岖,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼瓜富!你這毒婦竟也來了鳍咱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤与柑,失蹤者是張志新(化名)和其女友劉穎谤辜,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體价捧,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡丑念,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了结蟋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片脯倚。...
    茶點(diǎn)故事閱讀 40,133評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖嵌屎,靈堂內(nèi)的尸體忽然破棺而出推正,到底是詐尸還是另有隱情,我是刑警寧澤宝惰,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布植榕,位于F島的核電站,受9級特大地震影響尼夺,放射性物質(zhì)發(fā)生泄漏尊残。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一淤堵、第九天 我趴在偏房一處隱蔽的房頂上張望寝衫。 院中可真熱鬧,春花似錦拐邪、人聲如沸慰毅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽汹胃。三九已至,卻和暖如春乘陪,著一層夾襖步出監(jiān)牢的瞬間统台,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工啡邑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贱勃,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像贵扰,于是被迫代替她去往敵國和親仇穗。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評論 2 355

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

  • 本文出自 Eddy Wiki 戚绕,轉(zhuǎn)載請注明出處:http://eddy.wiki/interview-java.h...
    eddy_wiki閱讀 2,130評論 0 14
  • Java SE 基礎(chǔ): 封裝纹坐、繼承、多態(tài) 封裝: 概念:就是把對象的屬性和操作(或服務(wù))結(jié)合為一個(gè)獨(dú)立的整體舞丛,并盡...
    Jayden_Cao閱讀 2,110評論 0 8
  • layout: posttitle: 《Java并發(fā)編程的藝術(shù)》筆記categories: Javaexcerpt...
    xiaogmail閱讀 5,826評論 1 19
  • 本文主要講了java中多線程的使用方法耘子、線程同步、線程數(shù)據(jù)傳遞球切、線程狀態(tài)及相應(yīng)的一些線程函數(shù)用法谷誓、概述等。 首先講...
    李欣陽閱讀 2,456評論 1 15
  • Java多線程學(xué)習(xí) [-] 一擴(kuò)展javalangThread類 二實(shí)現(xiàn)javalangRunnable接口 三T...
    影馳閱讀 2,959評論 1 18