線程基礎(chǔ)、線程之間的共享和協(xié)作

1. 基礎(chǔ)概念

1.1 CPU核心數(shù)和線程數(shù)的關(guān)系

核心數(shù):

超線程 核心數(shù) 線程數(shù)
未使用 1 1
已使用 1 2

1.2 CPU時(shí)間片輪轉(zhuǎn)機(jī)制

又稱RR調(diào)度,會導(dǎo)致上下文切換

1.3 什么是進(jìn)程和線程

進(jìn)程:程序運(yùn)行資源分配的最小單位,進(jìn)程內(nèi)部有多個(gè)線程,會共享這個(gè)進(jìn)程的資源
線程:CPU調(diào)度的最小單位,必須依賴進(jìn)程而存在。

1.4 澄清并行和并發(fā)

并行:同一時(shí)刻,可以同時(shí)處理事情的能力
并發(fā):與單位時(shí)間相關(guān),在單位時(shí)間內(nèi)可以處理事情的能力

1.5 高并發(fā)編程的意義、好處和注意事項(xiàng)

好處:充分利用cpu的資源、加快用戶響應(yīng)的時(shí)間,程序模塊化,異步化
問題

  • 線程共享資源,存在沖突;
  • 容易導(dǎo)致死鎖;
  • 啟用太多的線程,就有搞垮機(jī)器的可能

2. 認(rèn)識Java里的線程

2.1 新啟線程的方式

三種創(chuàng)建線程方式:

  • Thread (繼承Thread類)
  • Runable (實(shí)現(xiàn)Runable接口)
  • Callable (實(shí)現(xiàn)Callable接口,run方法有返回值,可以使用Future接收返回值)

2.2 怎么樣才能讓Java里的線程安全停止工作呢

線程自然終止:自然執(zhí)行完或拋出未處理異常

  • stop(), resume(), suspend() 已不建議使用;
  • stop()會導(dǎo)致線程不會正確釋放資源;
  • suspend()容易導(dǎo)致死鎖。

Java線程是協(xié)作式,而非搶占式
調(diào)用一個(gè)線程的interrupt() 方法中斷一個(gè)線程,并不是強(qiáng)行關(guān)閉這個(gè)線程,只是跟這個(gè)線程打個(gè)招呼,將線程的中斷標(biāo)志位置為true,線程是否中斷,由線程本身決定。
isInterrupted() 判定當(dāng)前線程是否處于中斷狀態(tài)。
static方法interrupted() 判定當(dāng)前線程是否處于中斷狀態(tài),同時(shí)中斷標(biāo)志位改為false。
方法里如果拋出InterruptedException,線程的中斷標(biāo)志位會被復(fù)位成false,如果確實(shí)是需要中斷線程,要求我們自己在catch語句塊里再次調(diào)用interrupt()。


3. 對Java里的線程再多一點(diǎn)點(diǎn)認(rèn)識

3.1 線程常用方法和線程的狀態(tài)

線程只有5種狀態(tài)(New、Runable、Running、Blocked、Dead)。
整個(gè)生命周期就是這幾種狀態(tài)的切換。


線程狀態(tài)流轉(zhuǎn)圖

run()和start()
run()方法:普通對象的普通方法,
start()方法:Java才會將線程對象和操作系統(tǒng)中實(shí)際的線程進(jìn)行映射,再來執(zhí)行run方法。

yield() :讓出cpu的執(zhí)行權(quán),將線程從運(yùn)行轉(zhuǎn)到可運(yùn)行狀態(tài),但是下個(gè)時(shí)間片,該線程依然有可能被再次選中運(yùn)行。

3.2 線程的優(yōu)先級

取值為1~10,缺省為5,但線程的優(yōu)先級不可靠,不建議作為線程開發(fā)時(shí)候的手段

3.3 守護(hù)線程:

和主線程共死,finally中的代碼不能保證一定執(zhí)行

3.4 線程間的共享

synchronized內(nèi)置鎖
對象鎖:(修飾類的非靜態(tài)方法)鎖的是類的對象實(shí)例,(修飾代碼塊)鎖的是加鎖的實(shí)例。
類鎖:(修飾類的靜態(tài)方法)鎖的是每個(gè)類的的Class對象,每個(gè)類的的Class對象在一個(gè)虛擬機(jī)中只有一個(gè),所以類鎖也只有一個(gè)。

volatile關(guān)鍵字
適合于只有一個(gè)線程寫,多個(gè)線程讀的場景,因?yàn)樗荒艽_??梢娦?。

ThreadLocal
線程變量??梢岳斫鉃槭莻€(gè)map,類型 Map<Thread,Integer>

線程間協(xié)作
輪詢:難以保證及時(shí)性,資源開銷很大,
等待和通知

  • wait() 對象上的方法切換到runable狀態(tài)
  • notify() / notifyAll() 喚醒對象上的方法

等待和通知的標(biāo)準(zhǔn)范式
等待方
a. 獲取對象的鎖;
b. 循環(huán)里判斷條件是否滿足,不滿足調(diào)用wait方法,
c. 條件滿足執(zhí)行業(yè)務(wù)邏輯
通知方
a. 獲取對象的鎖;
b. 改變條件
c. 通知所有等待在對象的線程

notify和notifyAll應(yīng)該用誰?
應(yīng)該盡量使用notifyAll,使用notify因?yàn)橛锌赡馨l(fā)生信號丟失的的情況

等待超時(shí)模式實(shí)現(xiàn)一個(gè)連接池
假設(shè) 等待時(shí)間時(shí)長為T,當(dāng)前時(shí)間now+T以后超時(shí)

long overtime = now + T;
long remain = T; // 等待的持續(xù)時(shí)間
while ( result不滿足條件 && remain > 0) {
    wait(remain);
    remain = overtime – now; // 等待剩下的持續(xù)時(shí)間
}
return result;

join()方法
面試點(diǎn)
線程A,執(zhí)行了線程B的join方法,線程A必須要等待B執(zhí)行完成了以后,線程A才能繼續(xù)自己的工作

調(diào)用yield() 、sleep()、wait()、notify()等方法對鎖有何影響?
面試點(diǎn)

  • yield():讓出時(shí)間片,不會釋放鎖
  • sleep():線程進(jìn)入睡眠狀態(tài),不會釋放鎖
  • wait():調(diào)動(dòng)方法之前,必須要持有鎖。調(diào)用了wait()方法以后,鎖就會被釋放,進(jìn)入鎖的等待隊(duì)列,方法返回后重新拿到鎖
  • notify():調(diào)動(dòng)方法之前,必須要持有鎖,調(diào)用notify()方法本身不會釋放鎖的。而是通知等待隊(duì)列中的某一個(gè)線程,同步代碼塊執(zhí)行完畢后才會釋放鎖
  • notifyAll():同notify,有一點(diǎn)不同在于,notifyAll會發(fā)出n個(gè)信號(n=等待線程數(shù)),而notify只會發(fā)出一個(gè)信號,通常情況下,盡量選擇notifyAll
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容