JAVA——多线程死锁案例

什么是死锁

多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。

我们的线程是交错执行的,那么就很有可能出现以下的情况:

class Lipstick {//口红类
}
class Mirror {//镜子类
}
class Makeup extends Thread {//化妆类继承了Thread类
    boolean flag;
    String girl;
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();
 
       @Override
    public void run() {
        // TODO Auto-generated method stub
        doMakeup();
    }
 
    void doMakeup() {
        if (flag == 0) {
            synchronized (lipstick) {//需要得到口红的“锁”;
                System.out.println(girl + "拿着口红!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
 
                synchronized (mirror) {//需要得到镜子的“锁”;
                    System.out.println(girl + "拿着镜子!");
                }
 
            }
        } else {
            synchronized (mirror) {
                System.out.println(girl + "拿着镜子!");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lipstick) {
                    System.out.println(girl + "拿着口红!");
                }
            }
        }
    
}
 
public class TestDeadLock {
    public static void main(String[] args) {
        Makeup m1 = new Makeup();//丽丽的化妆线程;
        m1.girl = "丽丽";
        m1.flag = true;
        Makeup m2 = new Makeup();//玛丽的化妆线程;
        m2.girl = "玛丽";
        m2.flag = false;
        m1.start();
        m2.start();
    }
}
执行结果:
丽丽拿着口红
玛丽拿着镜子

  • 线程丽丽调用方法,得到口红锁
  • 同时线程玛丽调用方法,得到镜子锁
  • 线程丽丽和线程玛丽都继续执行,此时线程丽丽需要镜子锁才能继续往下执行。
  • 此时线程玛丽需要口红锁才能继续往下执行。
  • 但是:线程丽丽的口红锁并没有释放,线程玛丽的镜子锁也没有释放。
  • 所以他们都只能等待,而这种等待是无期限的–>永久等待–>死锁

造成死锁的原因可以概括成三句话:

  • 当前线程拥有其他线程需要的资源
  • 当前线程等待其他线程已拥有的资源
  • 都不放弃自己拥有的资源
    在这里插入图片描述

死锁的解决方案

加锁顺序:因为按照了不同顺序加锁,死锁便容易产生。所以如果在确保线程都是按照相同的顺序获得所,那就不存在此现象。

class Lipstick {//口红类
}
class Mirror {//镜子类
}
class Makeup extends Thread {//化妆类继承了Thread类
    boolean flag;
    String girl;
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();
 
       @Override
    public void run() {
        // TODO Auto-generated method stub
        doMakeup();
    }
 
    void doMakeup() {
        if (flag == 0) {
            synchronized (lipstick) {
                System.out.println(girl + "拿着口红!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
 
            }
            synchronized (mirror) {
                System.out.println(girl + "拿着镜子!");
            }
        } else {
            synchronized (mirror) {
                System.out.println(girl + "拿着镜子!");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            synchronized (lipstick) {
                System.out.println(girl + "拿着口红!");
            }
        }

}
 
public class TestDeadLock {
    public static void main(String[] args) {
        Makeup m1 = new Makeup();//丽丽的化妆线程;
        m1.girl = "丽丽";
        m1.flag = true;
        Makeup m2 = new Makeup();//玛丽的化妆线程;
        m2.girl = "玛丽";
        m2.flag = false;
        m1.start();
        m2.start();
    }
}
执行结果:
丽丽拿着口红
玛丽拿着镜子
玛丽拿着口红
丽丽拿着镜子

死锁检测

  • Jstack命令

jstack用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待,线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。

  • JConsole工具

Jconsole是JDK自带的监控工具,在JDK/bin目录下可以找到。它用于连接正在运行的本地或者远程的JVM,对运行在Java应用程序的资源消耗和性能进行监控,并画出大量的图表,提供强大的可视化界面。而且本身占用的服务器内存很小,甚至可以说几乎不消耗。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值