广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Java中死锁与活锁的具体实现
  • 854
分享到

Java中死锁与活锁的具体实现

2024-04-02 19:04:59 854人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

目录活锁与死锁活锁死锁死锁的四个必要条件互斥条件请求和保持条件不剥夺条件环路等待条件死锁示例死锁排查总结一下如何避免死锁预防死锁设置加锁顺序活锁示例解决活锁活锁与死锁 活锁 活锁同样

活锁与死锁

活锁

活锁同样会发生在多个相互协作的线程间,当他们为了彼此间的响应而相互礼让,使得没有一个线程能够继续前进,那么就发生了活锁。同死锁一样,发生活锁的线程无法继续执行。

相当于两个在半路相遇的人:出于礼貌他们相互礼让,避开对方的路,但是在另一条路上又相遇了。就这样,不停地一直避让下去。。。。

死锁

两个或更多线程阻塞着等待其它处于死锁状态的线程所持有的锁。死锁通常发生在多个线程同时但以不同的顺序请求同一组锁的时候,死锁会让你的程序挂起无法完成任务。

死锁的四个必要条件

在 Java 中使用多线程,就会有可能导致死锁问题。死锁会让程序一直住,不再程序往下执行。我们只能通过中止并重启的方式来让程序重新执行。这是我们非常不愿意看到的一种现象,我们要尽可能避免死锁的情况发生!

互斥条件

指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用完释放。

请求和保持条件

指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。

不剥夺条件

指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。

环路等待条件

指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{A,B,C,···,Z} 中的A正在等待一个B占用的资源;B正在等待C占用的资源,……,Z正在等待已被A占用的资源。

死锁示例


@SuppressWarnings("all")
public class DeadLock implements Runnable {
  public int flag = 1;

  
  private static final Object bread = new Object();

  
  private static final Object water = new Object();

  @Override
  public void run() {
    if (flag == 1) {
      // 先吃面包再喝水,环路等待条件
      synchronized (bread) {
        try {
          Thread.sleep(500);
        } catch (Exception e) {
          e.printStackTrace();
        }
        System.out.println("面包吃了,等喝水。。。");
        synchronized (water) {
          System.out.println("面包吃了,水也喝到了");
        }
      }
    }
    if (flag == 0) {
      // 先喝水再吃面包,环路等待条件
      synchronized (water) {
        try {
          Thread.sleep(500);
        } catch (Exception e) {
          e.printStackTrace();
        }
        System.out.println("喝完水,等吃面包了。。。");
        synchronized (bread) {
          System.out.println("喝完水,面包也吃完了。。。");
        }
      }
    }
  }

  public static void main(String[] args) {
    DeadLock lockBread = new DeadLock();
    DeadLock lockWater = new DeadLock();
    lockBread.flag = 1;
    lockWater.flag = 0;
    // lockBread,lockWater 都处于可执行状态,但 JVM 线程调度先执行哪个线程是不确定的。
    // lockWater 的 run() 可能在 lockBread 的 run() 之前运行
    new Thread(lockBread).start();
    new Thread(lockWater).start();
  }
}

程序运行结果:

喝完水,等吃面包了。。。
面包吃了,等喝水。。。

死锁排查

先执行上面的代码,再进行排查!

1、使用 jps -l

D:\workspace-code\gitee\dolphin>jps -l
7072 org.jetbrains.jps.cmdline.Launcher
8824 sun.tools.jps.Jps
17212
4012 com.dolphin.thread.locks.DeadLock
6684 org.jetbrains.idea.Maven.server.RemoteMavenServer36

4012 com.dolphin.thread.locks.DeadLock,粘贴进程号 4012

2、使用 jstack 4012

... ...
Java stack infORMation for the threads listed above:
===================================================
"Thread-1":
        at com.dolphin.thread.locks.DeadLock.run(DeadLock.java:40)
        - waiting to lock <0x000000076bf9e3D8> (a java.lang.Object)
        - locked <0x000000076bf9e3e8> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:748)
"Thread-0":
        at com.dolphin.thread.locks.DeadLock.run(DeadLock.java:26)
        - waiting to lock <0x000000076bf9e3e8> (a java.lang.Object)
        - locked <0x000000076bf9e3d8> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

从打印信息我们可以看出:

  • Found 1 deadlock: 发现一个死锁;
  • Thread-1 locked <0x000000076bf9e3e8> waiting to lock <0x000000076bf9e3d8>:线程1,锁住了 0x000000076bf9e3e8 等待 0x000000076bf9e3d8 锁;
  • Thread-0 locked <0x000000076bf9e3d8> waiting to lock <0x000000076bf9e3e8>:线程0,锁住了 0x000000076bf9e3d8 等待 0x000000076bf9e3e8 锁;

总结一下

  • 当 DeadLock 类的对象 flag=1 时(lockBread),先锁定 bread,睡眠500毫秒;
  • 而 lockBread 在睡眠的时候另一个 flag==0 的对象(lockWater)线程启动,先锁定 water,睡眠500毫秒;
  • lockBread 睡眠结束后需要锁定 water 才能继续执行,而此时 water 已被 lockWater 锁定;
  • lockWater 睡眠结束后需要锁定 bread 才能继续执行,而此时 bread 已被 lockBread 锁定;
  • lockBread、lockWater 相互等待,都需要得到对方锁定的资源才能继续执行,从而死锁;

如何避免死锁

预防死锁

其实就是破坏死锁的四个必要条件!!!

  • 破坏互斥条件:使资源同时访问而非互斥使用,就没有进程会阻塞在资源上,从而不发生死锁。
  • 破坏请求和保持条件:采用静态分配的方式,静态分配的方式是指进程必须在执行之前就申请需要的全部资源,且直至所要的资源全部得到满足后才开始执行,只要有一个资源得不到分配,也不给这个进程分配其他的资源。
  • 破坏不剥夺条件:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源,但是只适用于内存和处理器资源。
  • 破坏循环等待条件:给系统的所有资源编号,规定进程请求所需资源的顺序必须按照资源的编号依次进行。

设置加锁顺序

两个线程试图以不同的顺序来获得相同的锁,如果按照相同的顺序来请求锁,那么就不会出现循环的加锁依赖性,因此也就不会产生死锁,每个需要锁面包和锁水的线程都以相同的顺序来获取面包和水,那么就不会发生死锁了,如下图所示:

根据上图我们修改一下之前写的死锁代码,消除死锁!

@Override
public void run() {
    if (flag == 1) {
        // 先吃面包再喝水,环路等待条件
        synchronized (bread) {
            try {
                Thread.sleep(500);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("面包吃了,等喝水。。。");
        }

        synchronized (water) {
            System.out.println("面包吃了,水也喝到了");
        }
    }
    if (flag == 0) {
        // 先喝水再吃面包,环路等待条件
        synchronized (water) {
            try {
                Thread.sleep(500);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("喝完水,等吃面包了。。。");
        }

        synchronized (bread) {
            System.out.println("喝完水,面包也吃完了。。。");
        }
    }
}

程序运行结果:

喝完水,等吃面包了。。。
面包吃了,等喝水。。。
面包吃了,水也喝到了
喝完水,面包也吃完了。。。

这样就可以消除死锁发生~~~

活锁示例

import java.lang.management.ManagementFactory;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;


public class LiveLock {
  static ReentrantLock bread = new ReentrantLock();
  static ReentrantLock water = new ReentrantLock();

  public static void main(String[] args) {
    String name = ManagementFactory.getRuntimeMXBean().getName();
    String pid = name.split("@")[0];
    System.out.println("Pid is:" + pid);
    System.out.println("jstack " + pid);

    ExecutorService executorService = Executors.newCachedThreadPool();

    executorService.submit(
        () -> {
          try {
            // 尝试获得 bread 锁
            while (bread.tryLock(10, TimeUnit.SECONDS)) {
              System.out.println("拿到面包了,等着拿水。。。");
              TimeUnit.SECONDS.sleep(1);

              // 判断 water 是否被锁住,如果锁住,退出!再次尝试获取 water 锁
              boolean locked = water.isLocked();
              if (locked) {
                bread.unlock();
                continue;
              }

              // 尝试获得 water 锁
              boolean w = water.tryLock(10, TimeUnit.SECONDS);
              // 如果没有获取到 water 锁,释放 bread 锁,再次尝试!
              if (!w) {
                bread.unlock();
                continue;
              }
              System.out.println("拿到水了");
              break;
            }
            System.out.println("吃面包,喝水!");
          } catch (InterruptedException e) {
            System.out.println("线程中断");
          } finally {
            water.unlock();
            bread.unlock();
          }
        });

    executorService.submit(
        () -> {
          try {
            while (water.tryLock(10, TimeUnit.SECONDS)) {
              System.out.println("拿到水了,等着拿面包。。。");
              TimeUnit.SECONDS.sleep(2);

              // 判断 bread 是否被锁住,如果锁住,退出!再次尝试获取 bread 锁
              boolean locked = bread.isLocked();
              if (locked) {
                water.unlock();
                continue;
              }

              // 尝试获得 bread 锁
              boolean b = bread.tryLock(10, TimeUnit.SECONDS);
              // 如果没有获取到 bread 锁,释放 water 锁,再次尝试!
              if (!b) {
                water.unlock();
                continue;
              }
              System.out.println("拿到面包了");
              break;
            }
            System.out.println("喝水,吃面包!");
          } catch (InterruptedException e) {
            System.out.println("线程中断");
          } finally {
            bread.unlock();
            water.unlock();
          }
        });
    executorService.shutdown();
  }
}

程序运行结果:

Pid is:8788
jstack 8788
拿到面包了,等着拿水。。。
拿到水了,等着拿面包。。。
拿到面包了,等着拿水。。。
拿到水了,等着拿面包。。。
拿到面包了,等着拿水。。。
拿到面包了,等着拿水。。。
拿到水了,等着拿面包。。。

已经输出打印了 Pid,尝试自己用 jstack 调试一下吧!可以参考如何排查死锁的章节~

解决活锁

锁让出的时候添加随机睡眠时间

修改上面的程序,参考下面的代码:

executorService.submit(
        () -> {
          try {
            // 尝试获得 bread 锁
            while (bread.tryLock(10, TimeUnit.SECONDS)) {
              System.out.println("拿到面包了,等着拿水。。。");
              TimeUnit.SECONDS.sleep(1);

              // 判断 water 是否被锁住,如果锁住,退出!再次尝试获取 water 锁
              boolean locked = water.isLocked();
              if (locked) {
                bread.unlock();
                // 避免活锁
                TimeUnit.MILLISECONDS.sleep(1000);
                continue;
              }

              // 尝试获得 water 锁
              boolean w = water.tryLock(10, TimeUnit.SECONDS);
              // 如果没有获取到 water 锁,释放 bread 锁,再次尝试!
              if (!w) {
                bread.unlock();
                continue;
              }
              System.out.println("拿到水了");
              break;
            }
            System.out.println("吃面包,喝水!");
          } catch (InterruptedException e) {
            System.out.println("线程中断");
          } finally {
            water.unlock();
            bread.unlock();
          }
        });

executorService.submit(
    () -> {
        try {
            while (water.tryLock(10, TimeUnit.SECONDS)) {
                System.out.println("拿到水了,等着拿面包。。。");
                TimeUnit.SECONDS.sleep(2);

                // 判断 bread 是否被锁住,如果锁住,退出!再次尝试获取 bread 锁
                boolean locked = bread.isLocked();
                if (locked) {
                    water.unlock();
                    // 避免活锁
                    TimeUnit.MILLISECONDS.sleep(1500);
                    continue;
                }

                // 尝试获得 bread 锁
                boolean b = bread.tryLock(10, TimeUnit.SECONDS);
                // 如果没有获取到 bread 锁,释放 water 锁,再次尝试!
                if (!b) {
                    water.unlock();
                    continue;
                }
                System.out.println("拿到面包了");
                break;
            }
            System.out.println("喝水,吃面包!");
        } catch (InterruptedException e) {
            System.out.println("线程中断");
        } finally {
            bread.unlock();
            water.unlock();
        }
    });
executorService.shutdown();

程序输出结果:

Pid is:18256
jstack 18256
拿到面包了,等着拿水。。。
拿到水了,等着拿面包。。。
拿到面包了
喝水,吃面包!
拿到面包了,等着拿水。。。
拿到水了
吃面包,喝水!

Process finished with exit code 0

到此这篇关于Java中死锁与活锁的具体实现的文章就介绍到这了,更多相关Java 死锁与活锁内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Java中死锁与活锁的具体实现

本文链接: https://www.lsjlt.com/news/148145.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Java中死锁与活锁的具体实现
    目录活锁与死锁活锁死锁死锁的四个必要条件互斥条件请求和保持条件不剥夺条件环路等待条件死锁示例死锁排查总结一下如何避免死锁预防死锁设置加锁顺序活锁示例解决活锁活锁与死锁 活锁 活锁同样...
    99+
    2022-11-13
  • Java并发编程中死锁的实现
    这篇文章给大家介绍Java并发编程中死锁的实现,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。一、什么是死锁所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进二、死锁产生的...
    99+
    2023-06-15
  • MySQL中怎么实现死锁与日志
    本篇文章给大家分享的是有关MySQL中怎么实现死锁与日志,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。1、Case1:部分数据更新失败某天渠道...
    99+
    2022-10-18
  • java 中死锁问题的实例详解
    java 中死锁问题的实例详解先看代码在做解释public class DeadLock implements Runnable{ String a; String b; boolean flag; public DeadLock(...
    99+
    2023-05-31
    java 死锁 ava
  • MyIsam与InnoDB引擎的锁实现以及避免死锁产生的方法
    这篇文章主要讲解了“MyIsam与InnoDB引擎的锁实现以及避免死锁产生的方法”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“MyIsam与InnoDB引擎...
    99+
    2022-10-18
  • Java中避免出现死锁的方法有哪些
    今天就跟大家聊聊有关Java中避免出现死锁的方法有哪些,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。避免死锁的技术:加锁顺序加锁时限死锁检测加锁顺序当多个线程需要相同的一些锁,但是按...
    99+
    2023-05-31
    java 死锁 ava
  • MySQL的中事务与锁的实现
    本篇内容主要讲解“MySQL的中事务与锁的实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“MySQL的中事务与锁的实现”吧!MySQL 中事务的实现在关系型数...
    99+
    2022-10-18
  • 详解Java ReentrantReadWriteLock读写锁的原理与实现
    目录概述原理概述加锁原理图解过程源码解析解锁原理图解过程源码解析概述 ReentrantReadWriteLock读写锁是使用AQS的集大成者,用了独占模式和共享模式。本文和大家一起...
    99+
    2022-11-13
    Java ReentrantReadWriteLock读写锁 Java ReentrantReadWriteLock Java 读写锁
  • Java编程之多线程死锁与线程间通信简单实现代码
    死锁定义 死锁是指两个或者多个线程被永久阻塞的一种局面,产生的前提是要有两个或两个以上的线程,并且来操作两个或者多个以上的共同资源;我的理解是用两个线程来举例,现有线程A和B同时操作两个共同资源a和b,A操作a的时候上锁LockA,继续执行...
    99+
    2023-05-30
    java 多线程 ava
  • Java中多线程、线程同步与死锁的一些不为人知的秘密
    今天就跟大家聊聊有关Java中多线程、线程同步与死锁的一些不为人知的秘密,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。1.线程同步多线程引发的安全问题一个非常经典的案例,银行取钱的问...
    99+
    2023-05-31
    java 多线程 线程同步
  • Java中锁的实现原理和实例用法
    这篇文章主要介绍“Java中锁的实现原理和实例用法”,在日常操作中,相信很多人在Java中锁的实现原理和实例用法问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java中锁的实现原理和实例用法”的疑惑有所帮助!...
    99+
    2023-06-16
  • java中动态数组的具体实现
    声明:data为数组名。size为数组中最后一个元素的下一个位置。实现动态数组的原因:因为java中的数组是静态的,在new数组时就需要指定数组的大小,如果需要存储的元素为未知的个数,设置空间过大会造成浪费,设置空间过小会无法存入全部数据,...
    99+
    2019-01-23
    java教程 java 动态数组 实现
  • java中怎么实现可重入的自旋锁
    这篇文章主要介绍了java中怎么实现可重入的自旋锁的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇java中怎么实现可重入的自旋锁文章都会有所收获,下面我们一起来看看吧。说明是指试图获得锁的线程不会堵塞,而是通过...
    99+
    2023-06-30
  • Java中的synchronized锁膨胀机制怎么实现
    这篇文章主要讲解了“Java中的synchronized锁膨胀机制怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java中的synchronized锁膨胀机制怎么实现”吧!synch...
    99+
    2023-06-30
  • 浅谈js中Object.create()与new的具体实现与区别
    目录Object.create与new区别Object.create()原理new原理继承比较组合继承与寄生组合继承组合继承寄生组合继承Object.create与new区别 fun...
    99+
    2022-11-13
  • 一起聊聊Java中13种锁的实现方式
    目录1、悲观锁2、乐观锁3、分布式锁加锁4、可重入锁5、自旋锁6、独享锁7、共享锁8、读锁/写锁9、公平锁/非公平锁10、可中断锁/不可中断锁11、分段锁12、锁升级(无锁|偏向锁|...
    99+
    2022-11-13
    Java 实现锁 Java 锁
  • Java语言中cas指令的无锁编程实现实例
    最开始接触到相关的内容应该是从volatile关键字开始的吧,知道它可以保证变量的可见性,而且利用它可以实现读与写的原子操作。。。但是要实现一些复合的操作volatile就无能为力了。。。最典型的代表是递增和递减的操作。。。。我们知道,在并...
    99+
    2023-05-31
    java cas 无锁算法
  • Kubernetes中锁机制的设计与实现方法是什么
    这篇文章主要讲解了“Kubernetes中锁机制的设计与实现方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Kubernetes中锁机制的设计与实现...
    99+
    2022-10-19
  • Java中双重检查锁(double checked locking)的正确实现
    目录前言加锁 双重检查锁 错误的双重检查锁 隐患 正确的双重检查锁 总结前言 在实现单例模式时,如果未考虑多线程的情况,就容易写出下面的错误代码: public class Si...
    99+
    2022-11-12
  • 怎么在java中实现内置锁的可重入性
    这篇文章给大家介绍怎么在java中实现内置锁的可重入性,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。java基本数据类型有哪些Java的基本数据类型分为:1、整数类型,用来表示整数的数据类型。2、浮点类型,用来表示小数...
    99+
    2023-06-14
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作