死鎖簡介:
定義:多個線程同時等待其他線程釋放鎖,導致被無限期阻塞
原因:
- A線程持有鎖1,這時主內存的鎖1變量進入鎖定狀態,其他想獲得此變量的的線程必須等待。B線程持有鎖2,主內存中的鎖2變量進入鎖定狀態。
- 這時A線程再去獲取鎖2,B線程再去獲取鎖1,而此時A、B線程都沒有對原先鎖變量進行解鎖,故A線程等待B線程釋放鎖2,而B線程等待A線程釋放鎖1。
- 這時就出現了A、B線程同時被無限期阻塞,故導致死鎖
代碼實現:
package com.yitian.lock;
/**
* @Description TODO 實現死鎖
* @Author yitianRen
* @Date 2019/7/25 15:18
* @Version 1.0
**/
public class DeadLock {
protected static String locak1="1";
protected static String locak2="2";
public static void main(String[] args) {
Thread thread1=new Thread(new lock1());
Thread thread2=new Thread(new lock2());
thread1.start();
thread2.start();
}
}
class lock1 implements Runnable{
@Override
public void run() {
synchronized (DeadLock.locak1){
System.out.println("lock1:我獲得了第一個🔒");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (DeadLock.locak2){
System.out.println("lock1:我獲得了第二個🔒");
}
}
}
}
class lock2 implements Runnable{
@Override
public void run() {
synchronized (DeadLock.locak2){
System.out.println("lock2:我獲得了第一個🔒");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (DeadLock.locak1){
System.out.println("lock2:我獲得了第二個🔒");
}
}
}
}
避免方法:
- 加鎖順序:上述例子出現死鎖因爲A、B線程加鎖的順序不同,如果按照相同順序,則可以避免死鎖
public static void main(String[] args) {
Thread thread1 = new Thread(new lock1());
Thread thread2 = new Thread(new lock2());
thread1.start();
try {
thread1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.start();
}
- 加鎖時限:給鎖加一個超時時間,若一個線程沒有在給定的時限內成功獲得所有需要的鎖,則會進行回退並釋放所有已經獲得 的鎖,然後等待一段隨機的時間再重試。( 注:這種機制存在一個問題,在Java中不能對synchronized同步塊設置超時時間。你需要創建一個自定義鎖,或使用Java5中java.util.concurrent包下的工具)
- 死鎖檢測:死鎖檢測是一個更好的死鎖預防機制,它主要是針對那些不可能實現按序加鎖並且鎖超時也不可行的場景。(注: 還是上述例子,線程A持有鎖1,請求鎖2,線程B持有鎖2,請求鎖1。這時可以讓線程A去檢測線程B是否已經請求了線程A當前鎖持有的鎖,如果線程B的確是在請求,則線程A取消請求,並釋放鎖1,回退和等待。當然現實可能多條線程交叉,它需要遞進地檢測。)