java中線程死鎖的出現

之前參加工作時總是聽說某某功能出現了死鎖,經過簡要的詢問也算是認識到了死鎖.

關於"鎖"的出現這裏就不再贅述了,它的出現就是實現線程同步,維護數據的唯一性,使得多線程訪問共享資源是順序進行的

那麼死鎖是什麼呢?

我的理解是:所有線程都在等待根本不可能被釋放的鎖資源,導致工作無法正常進行

下面就來介紹下死鎖出現的四個條件↓↓↓↓

①互斥條件:

即當資源被一個線程使用(佔有)時,別的線程不能使用

②請求和保持條件:

即當資源請求者在請求其他的資源的同時保持對原有資源的佔有

③不剝奪條件:

資源請求者不能強制從資源佔有者手中奪取資源,資源只能由資源佔有者主動釋放

④環路等待條件:

指在發生死鎖時,必然存在一個進程——資源的環形鏈,即進程集合{P0,P1,P2,···,Pn}中的P0正在等待一個P1佔用的資源;P1正在等待P2佔用的資源,……,Pn正在等待已被P0佔用的資源

最近在刷面試題時遇到"手寫死鎖"的問題,說實話確實無從下手!

一.案例背景:

二.死鎖代碼及其測試結果展示:

public class DeadLock {
    //定義鎖A
    private static final Object cardA = new Object();
    //定義鎖B
    private static final Object cardB = new Object();

    /**
     * 穿過A房間進入B房間
     */
    public static void acrossRoomA(){
        synchronized (cardA){
            System.out.println(Thread.currentThread().getName()+"拿到cardA進入了A房間,等待cardB進入B房間....");
            try {
                Thread.sleep(500);
                acrossRoomB();//需要在A房間內穿越到房間B
                System.out.println(Thread.currentThread().getName()+"獲得了cardB,順利進入B房間!");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 穿過B房間進入A房間
     */
    public static void acrossRoomB(){
        synchronized (cardB){
            System.out.println(Thread.currentThread().getName()+"拿到cardB進入了B房間,等待cardA進入A房間....");
            try {
                Thread.sleep(500);
                acrossRoomA();//需要在B房間內穿越到房間A
                System.out.println(Thread.currentThread().getName()+"獲得了cardA,順利進入A房間!");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                acrossRoomA();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                acrossRoomB();
            }
        }).start();
    }
}

測試結果:

三.死鎖檢測方式

1.JDK命令行工具jps、jstack

①jps獲取當前運行的java進程的pid

②jstack+pid查看死鎖

2.使用Idea自帶的功能,點擊Dump Threads圖標

四.死鎖的解決辦法

這裏使用信號量Semaphore(可以自行查閱其API)去控制.

信號量可以控制資源能被多少線程訪問,這裏我們指定只能被一個線程訪問,就做到了類似鎖住。而信號量可以指定去獲取的超時時間,我們可以根據這個超時時間,去做一個額外處理。

對於無法成功獲取的情況,一般就是重複嘗試,或指定嘗試的次數,也可以馬上退出!

package deadlock;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class DeadLockSolution {

    //定義2個信號量
    public static final Semaphore licenceA = new Semaphore(1);
    //同一個時刻,只運行多少個進程同時運行指定代碼
    public static final Semaphore licenceB = new Semaphore(1);

    /**
     * 穿過A房間進入B房間
     */
    public static void acrossRoomA() {
        try {
            if (licenceA.tryAcquire(1,TimeUnit.SECONDS)){
                System.out.println(Thread.currentThread().getName()+"獲得A房間許可並進入,等待獲取進入B房間許可....");
                Thread.sleep(500);
                acrossRoomB();//需要在A房間內穿越到房間B
                System.out.println(Thread.currentThread().getName()+"獲得B房間許可,順利進入B房間!");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            licenceA.release();//釋放A房間許可
        }
    }

    /**
     * 穿過B房間進入A房間
     */
    public static void acrossRoomB() {
        try {
            if (licenceB.tryAcquire(1,TimeUnit.SECONDS)){
                System.out.println(Thread.currentThread().getName()+"獲得B房間許可並進入,等待獲取進入A房間許可....");
                Thread.sleep(500);
                acrossRoomA();//需要在B房間內穿越到房間A
                System.out.println(Thread.currentThread().getName()+"獲得A房間許可,順利進入A房間!");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            licenceB.release();//釋放B房間許可
        }
    }

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                acrossRoomA();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                acrossRoomB();
            }
        }).start();
    }
}

測試結果:

Thread-0獲得A房間許可並進入,等待獲取進入B房間許可....
Thread-1獲得B房間許可並進入,等待獲取進入A房間許可....
Thread-1獲得A房間許可,順利進入A房間!
Thread-0獲得B房間許可,順利進入B房間!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章