Java中多線程詳解(2)產生死鎖緣由及解決方法

1、什麼是死鎖多線程

死鎖理解起來很簡單,就是一個字,堵,下面圖中擁堵的十字路口就能夠看作一個死鎖的狀態,四個方向的車都要往前走,可是十字路口只有一個,只能容許一個方向的車經過後,才能讓另外一個方向的車經過。ide

在多線程中,四個方向的車流就能夠看作4個線程,而十字路口能夠看作一個資源對象,四個線程都要佔有它,會致使程序沒法正常的運行,這就叫死鎖,是否是很容易理解呢。post

用比較官方的解釋死鎖的話,就是經典的四大條件學習

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

二、不可搶佔,資源請求者不能強制從資源佔有者手中奪取資源,資源只能由資源佔有者主動釋放。線程

三、請求和保持,即當資源請求者在請求其餘的資源的同時保持對原有資源的佔有。code

四、循環等待,即存在一個等待隊列:P1佔有P2的資源,P2佔有P3的資源,P3佔有P1的資源。這樣就造成了一個等待環路。視頻

當上述四個條件都成立的時候,便造成死鎖。固然,死鎖的狀況下若是打破上述任何一個條件,即可讓死鎖消失。對象

2、建立死鎖案例教程

public class DeadLock {
    public static String r1="r1";
    public static String r2="r2";
    public static void main(String[] args) {
        Runable1 runable1=new Runable1();
        Runable2 runable2=new Runable2();
        Thread thread1=new Thread(runable1);
        Thread thread2=new Thread(runable2);
        thread1.start();
        thread2.start();
    }
}
class Runable1 implements Runnable{
    @Override
    public void run() {
        try {
             (DeadLock.r1) {
                System.out.println("r1的synchronized鎖住r1");
                Thread.sleep(3000);
                synchronized (DeadLock.r2) {
                    System.out.println("拿不到r2略略略");
                    Thread.sleep(60 * 1000);
                }
                System.out.println("被鎖住了,沒法打印");
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
}
class Runable2 implements Runnable{
    @Override
    public void run() {
        try {
            synchronized (DeadLock.r2){
                System.out.println("synchronized鎖住r2");
                Thread.sleep(3000);
                synchronized (DeadLock.r1){
                    System.out.println("拿不到r1略略略");
                    Thread.sleep(60 * 1000);
                }
                System.out.println("被鎖住了,沒法打印");
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
//運行結果
r1的synchronized鎖住r1
synchronized鎖住r2
複製代碼

在上面這個案例中,先建立了兩個資源對象r1和r2,而後建立了兩個線程類Runable1和Runable2,用synchronized方法去給資源添加鎖,使得兩個線程在執行完run方法以前都不會釋放資源對象r1和r2,(synchronized方法的做用就是一個同步鎖,代碼中放入synchronized括號中的資源在線程結束前都不會釋放,也就不能被其餘線程使用)以後用sleep方法讓線程阻塞(sleep(3000)的做用是是給線程能鎖住機會,sleep(60*1000)的做用是讓線程阻塞),這樣就形成了死鎖的現象,致使後面的語句沒法打印出來。

3、如何解決死鎖問題

1.在寫多線程操做時,最好不要一次鎖定多個對象,這樣就不會致使死鎖了。修改後的代碼以下。

public class DeadLock {
    public static String r1="r1";
    public static String r2="r2";
    public static void main(String[] args) {
        Runable1 runable1=new Runable1();
        Runable2 runable2=new Runable2();
        Thread thread1=new Thread(runable1);
        Thread thread2=new Thread(runable2);
        thread1.start();
        thread2.start();
    }
}
class Runable1 implements Runnable{
    @Override
    public void run() {
        try {
            synchronized (DeadLock.r1) {
                System.out.println("r1的synchronized鎖住r1");
                Thread.sleep(3000);
                    System.out.println("拿到r2");
                    Thread.sleep(3000);
                System.out.println("解鎖了,能夠打印");
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
}
class Runable2 implements Runnable{
    @Override
    public void run() {
        try {
            synchronized (DeadLock.r2){
                System.out.println("synchronized鎖住r2");
                Thread.sleep(3000); 
                   System.out.println("拿到r1");
                    Thread.sleep(3000);
                System.out.println("解鎖了,能夠打印");
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
//運行結果
r1的synchronized鎖住r1
synchronized鎖住r2
拿到r2
拿到r1
解鎖了,能夠打印
解鎖了,能夠打印
複製代碼

2.若是業務場景須要一次鎖定多個資源對象,能夠根據資源的某個屬性或者hashCode值來作比較,定義鎖的前後順序,下面是一個經過hashCode來定義鎖前後順序的例子。

public class DeadLock {
    public static String r1="r1";
    public static String r2="r2";
    public static void main(String[] args) {
        Runable1 runable1=new Runable1();
        Runable2 runable2=new Runable2();
        Thread thread1=new Thread(runable1);
        Thread thread2=new Thread(runable2);
        thread1.start();
        thread2.start();
    }
}
class Runable1 implements Runnable{
    @Override
    public void run() {
        try {
            if (DeadLock.r1.hashCode()>DeadLock.r2.hashCode()) {
                synchronized (DeadLock.r1) {
                    System.out.println("Runable1的synchronized鎖住r1");
                    Thread.sleep(3000);
                    synchronized (DeadLock.r2) {
                        System.out.println("Runable1的synchronized鎖住r2");
                        Thread.sleep(3000);
                    }
                    System.out.println("解鎖了,能夠打印");
                }
            }else{
                synchronized (DeadLock.r2) {
                    System.out.println("Runable1的synchronized鎖住r1");
                    Thread.sleep(3000);
                    synchronized (DeadLock.r1) {
                        System.out.println("Runable1的synchronized鎖住r2");
                        Thread.sleep(3000);
                    }
                    System.out.println("解鎖了,能夠打印");
                }
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
}
class Runable2 implements Runnable{
    @Override
    public void run() {
        try {
            if (DeadLock.r1.hashCode()<DeadLock.r2.hashCode()) {
                synchronized (DeadLock.r2) {
                    System.out.println("Runable2的synchronized鎖住r2");
                    Thread.sleep(3000);
                    synchronized (DeadLock.r1) {
                        System.out.println("Runable2的synchronized鎖住r1");
                        Thread.sleep(3000); 
                   }
                    System.out.println("解鎖了,能夠打印");
                }
            }else{
                synchronized (DeadLock.r1) {
                    System.out.println("Runable2的synchronized鎖住r1");
                    Thread.sleep(3000);
                    synchronized (DeadLock.r2) {
                        System.out.println("Runable2的synchronized鎖住r2");
                        Thread.sleep(3000);
                    }
                    System.out.println("解鎖了,能夠打印");
                }
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
}
//運行結果
Runable1的synchronized鎖住r1
Runable1的synchronized鎖住r2
解鎖了,能夠打印
Runable2的synchronized鎖住r1
Runable2的synchronized鎖住r2
解鎖了,能夠打印

參考:《2020最新Java基礎精講視頻教程和學習路線!》
連接:https://juejin.cn/post/694043...

相關文章
相關標籤/搜索