1.死鎖——在多線程(兩個或者兩個以上的線程)程式設計中,因為資源搶占而造成的線程無限等待的問題。
2.線程和鎖的關系:
一個線程可以擁有多個鎖,一個鎖隻能被一個線程擁有;
3.死鎖代碼
synchronized(lockA){
System.out.println("線程1 得到鎖A");
synchronized(lockB){
}
}
synchronized(lockB){
System.out.println("線程2 得到鎖B");
synchronized(lockA){
}
}
4.排查死鎖的工具 jconsole、 jmc、 vm
5.死鎖産生的4個條件(必須同時滿足)
(1)互斥條件——一個資源隻能被一個線程持有,當被一個線程持有之後,就不能被其它線程持有;——不可修改
(2)請求擁有條件——一個線程持有了一個資源之後又試圖請求另一個資源;——可以修改
(3)不可剝奪條件——一個資源在被一個線程持有之後,如果這個線程不釋放此資源,那麼其它線程不能強制獲得此資源;——不可修改
(4)環路等待條件——多個線程在擷取資源時形成了環形鍊;——可以修改
6.如何解決死鎖問題
從請求擁有條件和環形等待條件入手,修改任意一個:
(1)請求擁有條件
(2)環形等待條件——通過控制擷取鎖的順序來解決——破壞了環路等待條件;
7.線程通訊機制
線程通訊:一個線程的動作可以讓另一個線程感覺到就是線程通訊。
sleep()——線程休眠,必須傳遞一個明确的結束資料
wait()——線程休眠
notify()——喚醒線程
notifyAll()——喚醒全部線程
8.wait() 為什麼要加鎖?
wait()在使用的時候需要釋放鎖,在釋放鎖之前必須要有一把鎖,所有要加鎖;
wait()為什麼要釋放鎖?
wait() 預設是不傳遞任何資料值的,當不傳遞任何值的時候表示永久等待,這樣就造成一把鎖被一個線程一直持有,為了避免這種問題的方式,是以在使用wait時要釋放鎖。
9.注意事項
(1)使用wait() 、notify() 、notifyAll()等方法時必須加鎖;
(2)加鎖對象和wait() 、notify() 、notifyAll()的對象必須保持一緻;
(3)一組wait() 、notify() 、notifyAll()必須是同一對象;
(4)notifyAll()隻能喚醒目前對象的所有等待線程;
10.Thread.sleep(0)和Object.wait(0)的差別
(1)sleep是Thread的靜态方法;wait() 是Object的方法;
(2)sleep(0)——表示立即觸發一次CPU資源的搶占;wait(0)——永久的等待下去
(3)都需要處理throws InterruptedException異常;
11.wait和sleep的差別
(1)相同點:(2)不同點:
- 都可以讓目前線程休眠;
- 都必須處理Interrupt 異常;
- wait是Object 的方法,而sleep是Thread的靜态方法;
- 傳參不同:wait可以無參;sleep必須是一個大于等于0的參數;
- wait使用時必須加鎖;sleep使用時不用加鎖
- wait使用時會釋放鎖,而sleep不會釋放鎖;
- wait預設不傳參情況會進入WAITING狀态;sleep預設不傳參情況會進入TIME_WAITING狀态;
12.為什麼wait放到Object而不是Thread中?
wait操作必須要加鎖和釋放鎖,而鎖是屬于對象級别,而不是線程級别(線程和鎖是一對多的關系,即一個線程可以擁有多把鎖),為了靈活起見(一個線程當中會有多把鎖),就把wait放入Object。
13. 線程休眠和喚醒對象——LockSupport(wait的更新)
不需要加鎖和釋放鎖,也不需要處理異常;
LockSupport.park()——線程休眠——進入WAITING狀态;
LockSupport.unpark(線程名稱)——喚醒線程
14.wait和LockSupport的差別
(1)相同點(2)不同點:
- 兩者都可以使線程進行休眠;
- 兩者都可以傳參或者不傳參,并且二者線程狀态一緻;
- wait需要與Synchronized一起使用(必須加鎖),而LockSupport不需要加鎖;
- wait隻能喚醒全部和随機的一個線程;而LockSupport可以喚醒指定的線程;