美文网首页
十、Java高级特性(多线程死锁)

十、Java高级特性(多线程死锁)

作者: 大虾啊啊啊 | 来源:发表于2021-05-30 20:51 被阅读0次

一、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线程拿到水。他们在往下面再拿资源的时候,拿不到。然后就放弃原来的锁,重新拿。因此在程序上我们就做了一个时间的错开。这种一直尝试拿锁,又放弃锁的现象我们称为活锁。

相关文章

网友评论

      本文标题:十、Java高级特性(多线程死锁)

      本文链接:https://www.haomeiwen.com/subject/nnjssltx.html