【多线程】java线程死锁学习,包含案例

什么是线程死锁

定义

死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。

举例

假设有两个孩子分别为孩子A和孩子B,相当于两个线程,还有两个玩具,一个是苹果,一个是香蕉,一开始孩子A拿的是苹果,孩子B拿的是香蕉,之后过了一段时间,孩子A想要玩香蕉,孩子B想要玩苹果,但是他们都不愿意放下手中的玩具,结果就会导致双方都想要对方的玩具,但是又不放弃现在持有的玩具,就只能导致两方都在等待了,也就是我们所说的死锁了

package deadLock;
/**
 * @Author: WalkerShen
 * @DATE: 2022/3/15
 * @Description: 死锁
 **/
public class DeadLockDemo {

    /**
     * 模拟场景:
     * 线程0 获取资源1 休眠 获取资源2
     * 线程1 获取资源2 休眠 获取资源1
     */

    //定义两个资源
    //资源1
    private final static String apple="a";
    //资源2
    private final static String banana="b";

    public static void main(String[] args) {



        //定义线程A
        new Thread(()->{
            //使用synchronized进行加锁资源1
            synchronized (apple){
                System.out.println(Thread.currentThread().getName()+"获取苹果");
                //这里休眠1秒,确保线程B对资源2进行持有
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"等待获取香蕉");
                //此时,未曾释放资源1,但是想获取资源2的锁,但是资源2的锁已经被线程B持有,就会导致两方互相等待对方资源,导致死锁
                synchronized (banana){
                    System.out.println(Thread.currentThread().getName()+"获取香蕉");
                }
            }
        },"孩子A").start();


        //定义线程B
        new Thread(()-> {
            //使用synchronized进行加锁资源2
            synchronized (banana) {
                System.out.println(Thread.currentThread().getName() + "获取香蕉");
                //这里休眠1秒,确保线程A对资源1进行持有
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //加锁资源1
                System.out.println(Thread.currentThread().getName()+"等待获取苹果");
                synchronized (apple) {
                    System.out.println(Thread.currentThread().getName() + "获取苹果");
                }
            }
        },"孩子B").start();
    }

}

执行结果:

孩子A获取苹果
孩子B获取香蕉
孩子B等待获取苹果
孩子A等待获取香蕉

形成死锁的四个必要条件是什么

互斥条件:

  • 线程(进程)对于所分配到的资源具有排它性,
  • 即一个资源只能被一个线程(进程)占用,直到被该线程(进程)释放

请求与保持条件:

  • 一个线程(进程)因请求被占用资源而发生阻塞时,对已获得的资源保持不放。

不剥夺条件:

  • 线程(进程)已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。

循环等待条件:

  • 当发生死锁时,所等待的线程(进程)必定会形成一个环路(类似于死循环),造成永久阻塞

如何避免线程死锁 3`

我们只要破坏产生死锁的四个条件中的其中一个就可以了。

破坏互斥条件

这个条件我们没有办法破坏,因为我们用锁本来就是想让他们互斥的(临界资源需要互斥访问)

破坏请求与保持条件

一次性申请所有的资源。

package deadLock;

import java.util.ArrayList;
import java.util.List;

/**
 * @Author: WalkerShen
 * @DATE: 2022/3/15
 * @Description: 破坏请求和保持
 **/
public class BreakRequestAndKeep {


    /**
     * 孩子A和孩子B分别一次性持有所有的资源
     * 这里是将资源都放在了一个数组里面,然后进行持有
     */
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("apple");
        list.add("banana");

        new Thread(()->{
            synchronized (list){
                System.out.println(Thread.currentThread().getName()+"持有苹果和香蕉");
            }
        },"孩子A").start();


        new Thread(()->{
            synchronized (list){
                System.out.println(Thread.currentThread().getName()+"持有苹果和香蕉");
            }
        },"孩子B").start();
    }
}

破坏不剥夺条件

占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。

破坏循环等待条件

靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。

案例

这里先双方都先获取苹果,之后再获取香蕉
假设孩子A先获取到苹果,那么孩子B就会等待
之后孩子A会在持有苹果的过程中再去获取香蕉,获取完香蕉之后,会将两个玩具都放回原处,之后孩子B就可以去拿苹果和香蕉了

package deadLock;
/**
 * @Author: WalkerShen
 * @DATE: 2022/3/15
 * @Description: 破坏循环等待
 **/
public class BreakCircularWait {

    //定义两个资源
    //资源1
    private final static String apple="apple";
    //资源2
    private final static String banana="banana";

    public static void main(String[] args) {


        new Thread(()->{
            synchronized (apple){
                System.out.println(Thread.currentThread().getName()+"获取"+apple);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"等待"+banana);
                synchronized (banana){
                    System.out.println(Thread.currentThread().getName()+"获取"+banana);
                }
            }
        },"孩子A").start();


        new Thread(()-> {
            synchronized (apple) {
                System.out.println(Thread.currentThread().getName() +"获取"+ banana);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"等待"+banana);
                synchronized (banana) {
                    System.out.println(Thread.currentThread().getName() +"获取"+ banana);
                }
            }
        },"孩子B").start();
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WalkerShen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值