天天看點

【作業系統】——線程(三)

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)相同點:
  • 都可以讓目前線程休眠;
  • 都必須處理Interrupt 異常;
(2)不同點:
  • 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可以喚醒指定的線程;