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)的切換。

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