多线程的死锁问题

这篇具有很好参考价值的文章主要介绍了多线程的死锁问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

多线程的死锁问题,学习笔记,javaEE的学习,python,开发语言,java-ee,程序人生,学习,java

可重入和不可重入😊😊😊

一个线程针对同一个对象,连续加锁两次,是否会有问题 ~~ 如果没问题,就叫可重入的.如果有问题,就叫不可重入的.

代码示例🍉🍉🍉:

synchronized public void add(){
        synchronized (this){
            count++;
        }
}

解析😍😍😍:
锁对象是this,只要有线程调用add,进入add方法的时候,就会先加锁(能够加锁成功).紧接着又遇到了代码块,再次尝试加锁.站在this 的视角(锁对象),它认为自己已经被另外的线程给占用了,这里的第二次加锁是否要阻塞等待呢?🤔🤔🤔
如果第二次加锁成功,这个锁就是可重入的,Java中的synchronized 是“可重入锁”.
如果第二次加锁会阻塞等待,就是不可重入的,这样的锁称为“不可重入锁”.

这时新的情况就出现了😕😕😕:

场景如下: 一个线程没有释放锁, 然后又尝试再次加锁,这个锁是“不可重入锁”,第二次加锁的时候, 就会阻塞等待,直到第一次的锁被释放, 才能获取到第二个锁,但是释放第一个锁也是由该线程来完成, 结果这个线程已经躺平了, 啥都不想干了, 也就无法进行解锁操作,这时候就会死锁.
注: 博主后面的内容会重点讲解死锁.

Java 标准库中的线程安全类🍭🍭🍭

多个线程操作同一个集合类,就需要考虑到线程安全的事情.
1.Java 标准库中很多集合类都是线程不安全的,没有任何加锁措施.
比如:ArrayList,LinkedList,HashMap,TreeMap,HashSet,TreeSet,StringBuider.
2.但是还是有一些内置了synchronized加锁的集合类,线程是相对来说,更安全一点的,
比如: Vector(不推荐使用),HashTable(不推荐使用),StringBuffer,ConcurrentHashMap.
3.最后就是String它没有加锁,但是由于是不可变对象,不涉及修改,线程是绝对安全的

问题: 既然加锁了,线程会变得安全一些,为什么集合类都不加上锁了🤔🤔🤔🤔?
原因: (1).加锁这个是有副作用的,会产生额外的时间开销.
(2).对程序猿来说,有更多的选择空间,这些没内置加锁机制的集合类,当没有线程安全问题的时候,就可以放心用,当有线程安全问题的时候,可以手动加锁.但是像StringBuffer这些内置synchronized加锁的结合类,就没有这种选择,这也是为什么是Vector,HashTable不推荐使用的原因之一.

死锁❤️❤️❤️

~~ 死锁是一个非常影响我们幸福感问题.
一旦程序出现死锁,就会导致线程就跪了,无法继续执行后续工作了,程序势必会有严重bug,在我们写代码的时候,不经意间,就会写出死锁代码,并且这玩意还不容易测试出来.

死锁的三个典型情况🍁🍁🍁

1.一个线程,一把锁,连续加锁两次🍒🍒🍒

~~ 如果锁是“不可重入锁”,就会死锁.
注: Java里 synchronized 和 ReentrantLock 都是“可重入锁”.C++,Python,操作系统原生的加锁API都是不可重入的.

2.两个线程两把锁🍎🍎🍎

~~ 线程 t1 和线程 t2 各自先针对锁A和锁B加锁,再尝试获取对方的锁.
例子: (1)可以理解为你把家里门的钥匙锁在车里了,而车钥匙锁在家里了,最后,你不仅车开不了呢,家也回不去了
(2)有一个东北人和一个陕西人正坐在饺子馆的同一个餐桌上准备吃饺子,东北人吃饺子,喜欢蘸酱油,但他面前只有一瓶醋;陕西人吃饺子,喜欢蘸醋,但不巧的是他面前只有酱油;东北人说: “兄弟,你把酱油给我,我用完了之后给你醋”,但是陕西人也说:”老兄,你把醋给我,我用完了之后给你酱油”.假设哈,注意这是假设(现实生活一般不可能会这样)!!!如果这两人互不相让,此时就僵持住了.
例子2的代码:

public class ThreadDemo14 {
    public static void main(String[] args) {
        Object jiangyou = new Object();// jiangyou => 酱油
        Object cu = new Object();// cu => 醋
        Thread dongbeiren = new Thread(() -> {// dongbeiren => 东北人
            synchronized (cu) {
                try {
                    Thread.sleep(1000);// 确保两个线程都先把第一个锁拿到 => 线程是抢占式执行的
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (jiangyou) {
                    System.out.println("东北人把醋和酱油都拿到了");
                }
            }
        });
        Thread shanxiren = new Thread(() -> {// shanxiren => 陕西人
            synchronized (jiangyou) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (cu) {
                    System.out.println("陕西人把酱油和醋都拿到了");
                }
            }
        });
        dongbeiren.start();
        shanxiren.start();
    }
}

运行结果:

多线程的死锁问题,学习笔记,javaEE的学习,python,开发语言,java-ee,程序人生,学习,java

使用 jconsole 查看线程的情况

多线程的死锁问题,学习笔记,javaEE的学习,python,开发语言,java-ee,程序人生,学习,java

注: 针对死锁问题,是需要借助像 jconsole 这样的工具进行定位,看线程的状态和调用栈,从而分析出代码是在哪里死锁了.
多线程的死锁问题,学习笔记,javaEE的学习,python,开发语言,java-ee,程序人生,学习,java

多线程的死锁问题,学习笔记,javaEE的学习,python,开发语言,java-ee,程序人生,学习,java

3.多个线程,多把锁🍀🍀🍀

哲学家就餐问题 ~~ 教科书上的经典案例
多线程的死锁问题,学习笔记,javaEE的学习,python,开发语言,java-ee,程序人生,学习,java

想要解决死锁问题,就得分析一下死锁的形成.

死锁的四个必要条件(缺一不可)🌸🌸🌸:

1.互斥使用: 一个线程拿到一把锁之后,另一个线程就不能使用了(锁的基本特性).
2.不可抢占: 一个线程拿到锁之后,必须是这个线程主动释放,其它线程就获取不到了(墙角是挖不了滴).
3.请求和保持: 线程1拿到锁A之后,再去尝试获取锁B,这时线程1的A这把锁还是保持的,不会因为获取锁B就把锁A给释放了.
个人理解“吃着碗里的,惦记锅里的”,一个男生有了女朋友后,还去和其他女生搞暧昧,追求其他女生(渣男).
4.循环等待: 线程1尝试获取到锁A和锁B,线程2尝试获取到锁B和锁A,线程1在获取B的时候等待线程2的释放;同时,线程2在获取A的时候等待线程1释放锁A(或者可以理解家钥匙锁车里了,车钥匙锁家里了).

注: 条件1,2,3都是锁的基本特性,对于,synchronized这把锁来说,是无法改对.循环等待是这四个条件中唯一一个和代码结构相关的,也是作为程序猿的我们可以控制的.

解决死锁的方法其实很简单,就是破解循环等待这个必要条件🍃🍃🍃🍃:
针对锁进行编号,在需要同时获取多把锁的时候,约定加锁顺序,务必先对小的编号加锁,后对大的编号加锁(这是解决死锁,最简单可靠的办法).
注: 解决死锁,还有个银行家算法,本质上是对资源的更合理的分配,比较复杂,不适合在实际开发中使用,但是是学校操作系统课的期末考试必考题,建议上这门课的时候认真听一下,博主不在这讲解,因为我也不太理解,讲不来,(●’◡’●).
代码:

public class ThreadDemo14 {
    public static void main(String[] args) {
        // 假设 jiangyou 是 1 号, cu 是 2 号, 约定先拿小的, 后拿大的
        Object jiangyou = new Object();// jiangyou => 酱油
        Object cu = new Object();// cu => 醋
        Thread dongbeiren = new Thread(() -> {// dongbeiren => 东北人
            synchronized (cu) {
                try {
                    Thread.sleep(1000);// 确保两个线程都先把第一个锁拿到 => 线程是抢占式执行的
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (jiangyou) {
                    System.out.println("东北人把醋和酱油都拿到了");
                }
            }
        });
        Thread shanxiren = new Thread(() -> {// shanxiren => 陕西人
            synchronized (cu) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (jiangyou) {
                    System.out.println("陕西人把酱油和醋都拿到了");
                }
            }
        });
        dongbeiren.start();
        shanxiren.start();
    }
}

运行结果:

多线程的死锁问题,学习笔记,javaEE的学习,python,开发语言,java-ee,程序人生,学习,java

多线程当前的知识点梳理🎀🎀🎀

  1. 线程的基本概念.轻量,共享资源,节省了资申请的开销

  2. Thread类的使用

    1. 创建
      1. 继承Thread,重写run
      2. 实现Runnable,重写run
      3. 匿名内部类,继承Thread,重写run
      4. 匿名内部类,继承实现Runnable,重写run
      5. lambda表达式
    2. 终止
      1. 自己定义一个标志位
      2. 使用线程内置的标志位
        1. isInterruptted(),判断标志位是否为true
        2. interrupt(),设置标志位为true,还能以异常的方式唤醒sleep
      3. 其它线程只能通知该线程要终止了,这个线程是否真的终止,是它自己的事情~~
    3. 等待
      1. join ~~ 在线程a中,调用b.join(),此时就是线程a等待线程b结束.
      2. 阻塞
    4. 获取线程引用 ~~ Thread.currentThread();
    5. 休眠 ~~ sleep
  3. 线程状态

    1. NEW ~~ 初创状况
    2. TERMINATED ~~ 消亡状况
    3. RUNNABLE ~~ 就绪状况
    4. TIMED_WAITING ~~ 等待一段时间
    5. WAITING
    6. BLOCKED
  4. 线程安全

    一个多线程实际的执行顺序有多种变数 ~~ 线程安全就是在所有的变数下,都能够运行正确!!!

    1. 抢占式执行,随机调度

    2. 多个线程同时修改同一个变量

    3. 修改操作不是原子性的 ~~ 加锁(synchronized)

    4. 内存可见性问题

    5. 指令重拍序

  • 加锁 ~~ synchronized

关于加锁

  1. 修饰方法

    • 普通方法 ~~ 把锁加到this对象上
    • 静态方法 ~~ 把锁加到类对象上
  2. 修饰代码块 ~~ 手动指定加到那个对象上

  3. 锁对象

    1. 两个线程,针对同一个对象加锁,会发生锁冲突/锁竞争(产生阻塞等待)
    2. 两个线程,针对不同的对象加锁,不会有任何锁冲突
  4. 死锁

    1. 死锁的概念

    2. 死锁的三个典型情况

      1. 一个现场一把锁.连续加锁两次

      2. 两个线程两把锁,分别获取对方的锁

      3. N个线程,M把锁

    3. 可重入和不可重入

      • 线程针对同一个对象,连续加锁二次,是否会死锁

      • 会死锁,就叫可重入,不会死锁,就叫不可重入的.

      • 注: synchronized 是可重入的

    4. 死锁的四个必要条件

      • 最核心的就是“循环等待”

      • 解决: 在针对多把锁加锁的时候,约定好锁的顺序

    5. 如何破除死锁文章来源地址https://www.toymoban.com/news/detail-719907.html

到了这里,关于多线程的死锁问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【并发编程】多线程安全问题,如何避免死锁

    从今天开始阿Q将陆续更新 java并发编程专栏 ,期待您的订阅。 在系统学习线程之前,我们先来了解一下它的概念,与经常提到的进程做个对比,方便记忆。 线程和进程是操作系统中的两个重要概念,它们都代表了程序运行时的执行单位,它们的出现是为了更好地管理计算机

    2024年02月11日
    浏览(51)
  • 线程的状态,多线程带来的风险,synchronized关键字及死锁问题

    目录 状态  线程的意义 多线程带来的风险——线程安全✅ 线程安全的概念 线程不安全的原因 抢占式执行,随机性调度 修改共享数据 原子性-加🔒 可见性 指令重排序 解决线程不安全问题(学完线程再总结) synchronized——监视器锁monitor lock​编辑   互斥 使用示例

    2024年02月06日
    浏览(52)
  • Slave SQL线程与PXB FTWRL死锁问题分析

    2.27号凌晨生产环境MySQL备库在执行备份期间出现因FLUSH TABLES WITH READ LOCK未释放导致备库复制延时拉大,慢日志内看持锁接近25分钟未释放。 版本: MySQL 5.7.21 PXB 2.4.18 慢查询日志: 备份脚本中的备份命令: mysql_kill.sh的主要逻辑内容: 备份参数: 144是SQL线程,并行复制中的

    2024年04月29日
    浏览(65)
  • (17)线程的实例认识:wait,waitany,waitall,及经典死锁问题

         1、界面: 一个textbox,四个button。                       2、程序:前面(15)的book类与data类             1、t.Wait();         Task.Wait方法用于阻塞当前线程,并等待异步操作的完成。它是一个实例方法,针对具体的单一任务使用,用法:             task.Wait(

    2024年02月09日
    浏览(55)
  • 【JavaEE】并发编程(多线程)线程安全问题&内存可见性&指令重排序

    目录 第一个问题:什么是线程安全问题? 第二个问题:为什么会出现线程安全问题?  第三个问题:如何解决多线程安全问题?  第四个问题:产生线程不安全的原因有哪些?  第五个问题:内存可见性问题及解决方案  第六个问题:指令重排序问题? 线程安全就是多线程

    2024年02月01日
    浏览(65)
  • (学习笔记-进程管理)怎么避免死锁?

    在多线程编程中,我们为了防止多线程竞争共享资源而导致数据错乱,都会在操作共享资源之前加上互斥锁,只有成功获得到锁的线程,才能操作共享资源,获取不到锁的线程就只能等待,直到锁被释放。 那么,当两个线程为了保护两个不同的共享资源而使用了两个互斥锁,

    2024年02月12日
    浏览(54)
  • 【JavaEE面试题(九)线程安全问题的原因和解决方案】

    大家观察下是否适用多线程的现象是否一致?同时尝试思考下为什么会有这样的现象发生呢? 原因是 1.load 2. add 3. save 注意:可能会导致 小于5w 想给出一个线程安全的确切定义是复杂的,但我们可以这样认为: 如果多线程环境下代码运行的结果是符合我们预期的,即在单线

    2024年02月16日
    浏览(47)
  • 【JAVAEE学习】探究Java中多线程的使用和重点及考点

    ˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好,我是xiaoxie.希望你看完之后,有不足之处请多多谅解,让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如需转载还请通知˶⍤⃝˶​ 个人主页:xiaoxieʕ̯

    2024年04月15日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包