iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >多线程的死锁问题
  • 452
分享到

多线程的死锁问题

python开发语言java-ee程序人生学习java 2023-10-02 06:10:51 452人浏览 泡泡鱼

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

摘要

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

fly

可重入和不可重入😊😊😊

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

代码示例🍉🍉🍉:

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();    }}

运行结果:

image-20230926104443616

使用 jconsole 查看线程的情况

image-20230926112333971

注: 针对死锁问题,是需要借助像 jconsole 这样的工具进行定位,看线程的状态和调用栈,从而分析出代码是在哪里死锁了.
image-20230926113122179

image-20230926113420416

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

哲学家就餐问题 ~~ 教科书上的经典案例
image-20230926124320039

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

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

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();    }}

运行结果:

image-20230926143143122

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

  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://blog.csdn.net/m0_73740682/article/details/133310787

--结束END--

本文标题: 多线程的死锁问题

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

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

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

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

下载Word文档
猜你喜欢
  • 多线程的死锁问题
    可重入和不可重入😊😊😊 一个线程针对同一个对象,连续加锁两次,是否会有问题 ~~ 如果没问题,就叫可重入的.如果有问题,就叫不可重入的. 代码示例&#...
    99+
    2023-10-02
    python 开发语言 java-ee 程序人生 学习 java
  • python多线程互斥锁与死锁问题详解
    目录一、多线程共享全局变量二、给线程加一把锁锁三、死锁问题总结一、多线程共享全局变量 代码实现的功能: 创建work01与worker02函数,对全局变量进行加一操作创建main函数...
    99+
    2024-04-02
  • 如何解决Java多线程死锁问题
    死锁问题 死锁定义 多线程编程中,因为抢占资源造成了线程无限等待的情况,此情况称为死锁。 死锁举例 注意:线程和锁的关系是:一个线程可以拥有多把锁,一个锁只能被一个线程拥有。 当两个...
    99+
    2024-04-02
  • Java多线程之死锁问题,wait和notify
    文章目录 一. synchronnized 的特性1. 互斥性2. 可重入性 二. 死锁问题1. 什么是死锁2. 死锁的四个必要条件3. 常见的死锁场景及解决3.1 不可重入造成的死锁3....
    99+
    2023-09-13
    java 死锁 wait notify synchronized
  • java多线程死锁问题的详细介绍
    本篇内容主要讲解“java多线程死锁问题的详细介绍”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“java多线程死锁问题的详细介绍”吧!一、什么是死锁当两个或两个...
    99+
    2024-04-02
  • Java多线程死锁问题怎么解决
    解决Java多线程死锁问题的常用方法有以下几种:1. 避免使用多个锁:尽量减少使用多个锁来降低出现死锁的概率。2. 按照固定的顺序获...
    99+
    2023-09-22
    Java
  • Java多线程死锁问题详解(wait和notify)
    目录一. synchronnized 的特性1. 互斥性2. 可重入性二. 死锁问题1. 什么是死锁2. 死锁的四个必要条件3. 常见的死锁场景及解决3.1 不可重入造成的死锁3.2...
    99+
    2023-01-05
    Java多线程死锁 java死锁的原因及解决方法 java多线程死锁问题
  • 多线程update导致的mysql死锁问题处理方法
    最近想起之前处理过的一个mysql 死锁问题,是在高并发下update批量更新导致的,这里探讨一下发生的原因,以及解决办法; 发生死锁的sql语句如下,其中where条件后的字段是有复合索引的。 update t_push_mes...
    99+
    2023-09-06
    数据库 java 开发语言 mysql死锁问题 mysql
  • Java 线程死锁的问题解决办法
     Java 线程死锁的问题解决办法【线程死锁】 原因:两个线程相互等待被对方锁定的资源 代码模拟:public class DeadLock { public static void main(String[] arg...
    99+
    2023-05-31
    java 线程死锁 ava
  • 如何解决 C++ 多线程编程中常见的死锁问题?
    如何解决 c++++ 多线程编程中的常见死锁问题?避免死锁的技术:加锁顺序:始终以相同的顺序获取锁。死锁检测:使用算法检测并解决死锁。超时:为锁设置超时值,防止线程无限期等待。优先级反转...
    99+
    2024-05-13
    多线程编程 死锁 c++
  • C语言多线程开发中死锁与读写锁问题详解
    目录死锁读写锁死锁 有时,一个线程需要同时访问两个或更多不同的共享资源,而每个资源又都由不同的互斥量管理。当超过一个线程加锁同一组互斥量时,就有可能发生死锁; 两个或两个以上的进程在...
    99+
    2024-04-02
  • python多线程互斥锁与死锁
    目录一、多线程间的资源竞争二、互斥锁1.互斥锁示例2.可重入锁与不可重入锁三、死锁一、多线程间的资源竞争 以下列task1(),task2()两个函数为例,分别将对全局变量num加一...
    99+
    2024-04-02
  • 多线程之死锁详解
    死锁是指两个或多个线程在执行过程中,因争夺资源而造成的一种互相等待的现象,如果无外力干涉,这些线程将无法继续执行下去。死锁的产生通常...
    99+
    2023-09-13
    多线程
  • C++多线程之互斥锁与死锁
    目录1.前言2.互斥锁2.1 互斥锁的特点2.2 互斥锁的使用2.3 std::lock_guard3.死锁3.1 死锁的含义3.2 死锁的例子3.3 死锁的解决方法1.前言 比如说...
    99+
    2024-04-02
  • C语言多线程开发中死锁与读写锁问题怎么解决
    今天小编给大家分享一下C语言多线程开发中死锁与读写锁问题怎么解决的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。死锁有时,一个...
    99+
    2023-06-30
  • Java多线程之死锁详解
    目录1、死锁2、死锁经典问题——哲学家就餐问题 总结1、死锁 出现场景:当线程A拥有了A对象的锁,想要去获取B对象的锁;线程B拥有了B对象的锁,想要拥有A对象的锁,两个线程...
    99+
    2024-04-02
  • Java线程技术中的死锁问题怎么解决
    这篇文章主要介绍“Java线程技术中的死锁问题怎么解决”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Java线程技术中的死锁问题怎么解决”文章能帮助大家解决问题。我们知道,使用 synchroniz...
    99+
    2023-06-02
  • 如何避多线程产生死锁
    这篇文章将为大家详细讲解有关如何避多线程产生死锁,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。一、死锁的定义多线程以及多进程改善了系统资源的利用率并提高了系统 的处理能力。然而,并发执行也带...
    99+
    2023-05-31
    多线程 死锁
  • java多线程死锁如何解决
    Java中死锁的解决办法有以下几种:1. 避免使用多个锁:当多个线程需要获取多个锁时,可以尝试将多个锁合并为一个锁,或者将一个锁拆分...
    99+
    2023-08-24
    java
  • 死锁问题详解
    目录前言资源可抢占资源和不可抢占资源资源获取死锁资源死锁的条件死锁模型鸵鸟算法死锁检测和恢复从死锁中恢复通过抢占进行恢复通过回滚进行恢复杀死进程恢复死锁避免单个资源的银行家算法破坏死...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作