IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 并发的死锁问题及解决方案 -> 正文阅读

[Java知识库]并发的死锁问题及解决方案

目录

1.什么是死锁

2.发生死锁的例子

2.1简单的例子

2.2生产中的例子-转账

2.3模拟多人转账

3.死锁的4个必要条件

4.如何定位死锁

5.修复死锁的策略

5.1线上发生死锁怎么办?

5.2常见修复的策略

6.实际工作中如何避免死锁


1.什么是死锁

  • 发生在并发中
  • 互不相让:当两个(或更多)线程(或进程)相互持有对方所需要的资源,又不主动释放,导致所有人都无法继续前进,导致程序陷入无尽的阻塞,这就是死锁

2.发生死锁的例子

2.1简单的例子

  • 当类的对象flag=1时(T1),先锁定O1,睡眠500毫秒,然后锁定O2
  • 而T1在睡眠的时候另一个flag=0的对象(T2)启动,先锁定O2,睡眠500毫秒,等待T1释放O1
  • T1睡眠结束后需要锁定O2才能继续执行,而此时O2已被T2锁定
  • T2睡眠结束后需要锁定 O1才能继续执行,而此时O1已被T1锁定
  • T1和T2互相等待,都需要对方多订的资源才能继续执行,从而产生死锁
public class MustDeadLock implements Runnable {

    int flag = 1;
    static Object o1 = new Object();
    static Object o2 = new Object();

    public static void main(String[] args) {
        MustDeadLock r1 = new MustDeadLock();
        MustDeadLock r2 = new MustDeadLock();
        r1.flag = 1;
        r2.flag = 0;
        new Thread(r1).start();
        new Thread(r2).start();
    }

    @Override
    public void run() {
        if (flag == 1){
            synchronized (o1){
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o2){
                    System.out.println("r1成功拿到两把锁");
                }
            }
        }

        if (flag == 0){
            synchronized (o2){
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o1){
                    System.out.println("r2成功拿到两把锁");
                }
            }
        }
    }
}

2.2生产中的例子-转账

  • 需要两把锁
  • 获取两把锁成功,且余额大于0,则扣除转出人,增加收款人的余额,是原子操作
  • 顺序相反导致死锁
public class TransferMoney implements Runnable{

    int flag = 1;

    static Account a = new Account(500);
    static Account b = new Account(500);

    public static void main(String[] args) throws InterruptedException {
        TransferMoney r1 = new TransferMoney();
        TransferMoney r2 = new TransferMoney();
        r1.flag = 1;
        r2.flag = 0;
        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r2);
        t1.start();
        t2.start();
        t1.join();
        t1.join();
        System.out.println("a的余额"+a.balance);
        System.out.println("b的余额"+b.balance);
    }

    @Override
    public void run() {
        if (flag ==1){
            transferMoney(a,b,200);
        }
        if (flag ==0){
            transferMoney(b,a,200);
        }
    }

    public static void transferMoney(Account from, Account to, int amount) {

        synchronized (from){
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (to){
                if (from.balance - amount <0){
                    System.out.println("余额不足,转账失败");
                }
                from.balance-=amount;
                to.balance+=amount;
                System.out.println("转账成功");
            }
        }
    }

    static class Account{
        int balance;

        public Account(int balance) {
            this.balance = balance;
        }
    }

}

2.3模拟多人转账

public class MultTransferMoney {
    public static final int NUM_ACCOUNTS = 500;
    public static final int NUM_MONEY = 1000;
    public static final int NUM_ITERATIONS = 1000;
    public static final int NUM_THREADS = 20;

    public static void main(String[] args) {
        final Random rnd = new Random();
        final Account[] accounts = new Account[NUM_ACCOUNTS];
        for (int i = 0; i < accounts.length; i++) {
            accounts[i] = new Account(NUM_MONEY);
        }
        class TransferThread extends Thread{
            @Override
            public void run(){
                for (int i = 0; i < NUM_ITERATIONS; i++) {
                    int fromAcct = rnd.nextInt(NUM_ACCOUNTS);
                    int toAcct = rnd.nextInt(NUM_ACCOUNTS);
                    int amount = rnd.nextInt(NUM_MONEY);
                    TransferMoney.transferMoney(accounts[fromAcct],accounts[toAcct],amount);
                }
            }
        }
        for (int i = 0; i < NUM_THREADS; i++) {
            new TransferThread().start();
        }
    }
}

3.死锁的4个必要条件

  • 互斥条件
  • 请求与保持条件
  • 不剥夺条件
  • 循环等待条件

4.如何定位死锁

使用ThreadMaxBean

public class ThreadMXBeanD implements Runnable{
    int flag = 1;
    static Object o1 = new Object();
    static Object o2 = new Object();

    public static void main(String[] args) {
        ThreadMXBeanD r1 = new ThreadMXBeanD();
        ThreadMXBeanD r2 = new ThreadMXBeanD();
        r1.flag = 1;
        r2.flag = 0;
        new Thread(r1).start();
        new Thread(r2).start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        ThreadMXBean threadMXBean =  ManagementFactory.getThreadMXBean();
        final long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();
        if (deadlockedThreads != null && deadlockedThreads.length > 0){
            for (int i = 0; i < deadlockedThreads.length; i++) {
                final ThreadInfo threadInfo = threadMXBean.getThreadInfo(deadlockedThreads[i]);
                // 发生死锁Thread-1
                // 发生死锁Thread-0
                System.out.println("发生死锁"+threadInfo.getThreadName());
            }
        }

    }

    @Override
    public void run() {
        if (flag == 1){
            synchronized (o1){
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o2){
                    System.out.println("r1成功拿到两把锁");
                }
            }
        }

        if (flag == 0){
            synchronized (o2){
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o1){
                    System.out.println("r2成功拿到两把锁");
                }
            }
        }
    }
}

5.修复死锁的策略

5.1线上发生死锁怎么办?

  • 线上问题都需要防患于未然,不造成损失扑灭几乎已经是不可能
  • 保存案发现场然后重启服务器
  • 暂时保证线上服务的安全,然后在利用刚才保存的信息,排查死锁,修改代码,重新发版

5.2常见修复的策略

  • 避免策略:哲学家就餐的换手方案,转账换序方案
  • 检测与恢复策略:一段时间检测是否有死锁,如果有就剥夺某一个资源,就打开死锁
  • 鸵鸟策略:如果我们发生死锁的概率非常低,那么我就直接忽略它,知道死锁的发生的时候,再人生修复

6.实际工作中如何避免死锁

  1. 设置超时时间
  2. 多食用并发类而不是自己设计类
  3. 尽量降低所得使用粒度,用不同的锁而不是同一个锁
  4. 如果能使用同步代码块,就不使用同步方法,自己指定锁对象
  5. 给你的线程起个有意义的名字,debug和排查的时事半功倍,框架和JDK都遵守这个最佳实践
  6. 避免锁的嵌套:MustDeadLock
  7. 分配资源前先看能不能收回来
  8. 尽量不要几个功能用同一把锁:专锁专用
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-11-15 15:42:36  更:2021-11-15 15:44:17 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 3:04:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码