十、Java高級特性(多線程死鎖)

一、synchronized死鎖

我們先來看以下代碼

示例

  • A線程
package com.it.test.thread.consumer_product.dielock;

public class AThread extends Thread{

    private Food food;
    private Water water;

      public AThread(Food food, Water water) {
        this.food = food;
        this.water = water;
    }

    @Override
    public void run() {
        synchronized (water){
            System.out.println("AThread拿到水了");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (food){
                System.out.println("AThread拿到食物了");
            }

        }
    }
}

  • B線程

public class BThread extends Thread{

    private Food food;
    public Water water;

    public BThread(Food food, Water water) {
        this.food = food;
        this.water = water;
    }

    @Override
    public void run() {
        synchronized (food){
            System.out.println("BThread拿到食物了");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (water){
                System.out.println("BThread拿到水了");
            }

        }
    }
}

  • 測試
package com.it.test.thread.consumer_product.dielock;

public class DieLockTest {
    public static void main(String[] args) {
        Food food = new Food();
        Water water = new Water();

        AThread aThread = new AThread(food,water);

        BThread bThread = new BThread(food,water);
        aThread.start();
        bThread.start();
    }
}

AThread拿到水了
BThread拿到食物了

代碼的設計是A線程拿到水,接着去拿食物,然後執行完畢。B線程先拿食物,然後拿水,然後執行完畢。
但是由於A線程和B線程拿線程的資源不對,導致A線程拿到了水之後,一直在等食物,卻沒有釋放水。B線程拿到食物之後,一直在等待水,沒有釋放食物。導致兩個線程處於一直等待拿鎖的狀態。這樣就造成了死鎖

解決辦法

我們將A線程和B線程拿資源的順序修改一下,使得他們都是先拿水,在拿食物試試。

package com.it.test.thread.consumer_product.dielock;

public class AThread extends Thread{

    private Food food;
    private Water water;

      public AThread(Food food, Water water) {
        this.food = food;
        this.water = water;
    }

    @Override
    public void run() {
        synchronized (water){
            System.out.println("AThread拿到水了");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (food){
                System.out.println("AThread拿到食物了");
            }

        }
    }
}

package com.it.test.thread.consumer_product.dielock;

public class BThread extends Thread{

    private Food food;
    public Water water;

    public BThread(Food food, Water water) {
        this.food = food;
        this.water = water;
    }

    @Override
    public void run() {
        synchronized (water){
            System.out.println("BThread拿到水了");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (food){
                System.out.println("BThread拿到食物了");
            }

        }
    }
}

AThread拿到水了
AThread拿到食物了
BThread拿到水了
BThread拿到食物了

二、產生死鎖的條件

(1)多個線程搶佔多個資源
(2)搶佔資源的順序不對
(3)拿到資源不放手

三、使用ReentrantLock嘗試拿鎖的機制解決死鎖問題

  • 創建兩個線程爭奪的資源實體,繼承ReentrantLock。
package com.it.test.thread.consumer_product.dielock;

import java.util.concurrent.locks.ReentrantLock;

public class Food extends ReentrantLock {
}

package com.it.test.thread.consumer_product.dielock;

import java.util.concurrent.locks.ReentrantLock;

public class Water extends ReentrantLock {
}

  • 創建兩個線程競爭嘗試獲取資源
  • AThread
package com.it.test.thread.consumer_product.dielock;

import java.util.Random;

public class AThread extends Thread{

    private Food food;
    private Water water;

      public AThread(Food food, Water water) {
        this.food = food;
        this.water = water;


      }

    @Override
    public void run() {
        Random r = new Random();
        while (true)
        {
            if(food.tryLock())
            {
                System.out.println("AThread拿到食物了");
                if(water.tryLock()){
                    System.out.println("AThread拿到水了");
                    food.unlock();
                    water.unlock();
                    //跳出循環
                    break;
                }
                else{
                    System.out.println("AThread沒有拿到水,放棄了食物");
                    food.unlock();
                }
            }

            try {
                Thread.sleep(r.nextInt(3));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

  • BThread
package com.it.test.thread.consumer_product.dielock;

public class BThread extends Thread{

    private Food food;
    public Water water;

    public BThread(Food food, Water water) {
        this.food = food;
        this.water = water;

    }

    @Override
    public void run() {
        while (true){
            if(water.tryLock())
            {
                System.out.println("BThread拿到水了");
                if(food.tryLock())
                {
                    System.out.println("BThread拿到食物了");
                    food.unlock();
                    water.unlock();
                    //跳出循環
                    break;
                }
                else{
                    System.out.println("BThread沒有拿到食物,放棄了水");
                    water.unlock();
                }
            }
        }

    }
}

  • 測試
package com.it.test.thread.consumer_product.dielock;

public class DieLockTest {
    public static void main(String[] args) {
        Food food = new Food();
        Water water = new Water();

        AThread aThread = new AThread(food,water);

        BThread bThread = new BThread(food,water);
        aThread.start();
        bThread.start();
    }
}

BThread拿到水了
AThread拿到食物了
BThread沒有拿到食物,放棄了水
AThread沒有拿到水,放棄了食物
BThread拿到水了
BThread沒有拿到食物,放棄了水
AThread拿到食物了
AThread沒有拿到水,放棄了食物
BThread拿到水了
BThread拿到食物了
AThread拿到食物了
AThread拿到水了

在最後A線程和B線程都拿到了食物和水。
在嘗試拿鎖的機制中,A線程和B線程中都開啓了一個while循環,只有水和食物都拿到之後,才跳出循環。A線程嘗試先拿食物,拿到食物之後嘗試拿水,如果拿不到水,則將原來的食物也丟棄。B線程也一樣,嘗試先拿食物,拿到食物後再拿水,如果拿不到水,則將原來的食物也丟棄。在嘗試拿鎖的過程,我們給A線程做出一點點隨機的休眠時間,錯開。最後A和B線程都會拿到食物和水。
嘗試拿鎖的機制,就會產生一個問題就是:每次嘗試拿鎖的時候,A線程都是先拿到食物,B線程拿到水。他們在往下面再拿資源的時候,拿不到。然後就放棄原來的鎖,重新拿。因此在程序上我們就做了一個時間的錯開。這種一直嘗試拿鎖,又放棄鎖的現象我們稱爲活鎖。

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