Java并发-线程上下文切换与死锁

news2024/10/6 18:31:12

 理解线程的上下文切换

概述:在多线程编程中,线程个数一般都大于 CPU 个数,而每个 CPU 同一时-刻只能被一个线程使用,为了让用户感觉多个线程是在同时执行的, CPU 资源的分配采用了时间片轮转的策略,也就是给每个线程分配一个时间片,线程在时间片内占用 CPU 执行任务

 定义:当前线程使用完时间片后,就会处于就绪状态并让出 CPU,让其他线程占用,这就是上下文切换,从当前线程的切换到了其他线程

线程上下文切换时机: 当前线程的 CPU 时间片使用完或者是当前线程被其他线程中断时,当前线程就会释放执行权。那么此时执行权就会被切换给其他的线程进行任务的执行,一个线程释放,另外一个线程获取,就是我们所说的上下文切换时机。

什么是线程死锁

死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的互相等待的现象,在无外力作用的情况下,这些线程会一直相互等待而无法继续运行下去

  •  如上图所示死锁状态,线程 A 己经持有了资源 2,它同时还想申请资源 1,可是此时线程 B 已经持有了资源 1 ,线程 A 只能等待。
  • 反观线程 B 持有了资源 1 ,它同时还想申请资源 2,但是资源 2 已经被线程 A 持有,线程 B 只能等待。所以线程 A 和线程 B 就因为相互等待对方已经持有的资源,而进入了死锁状态。

 线程死锁的必备要素

  • 互斥条件:进程要求对所分配的资源进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待;

两个线程拥有互斥的资源而且都没有释放。

  • 不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(只能是主动释放,如 yield 释放 CPU 执行权);

没有外力强迫其释放,自己也不能主动释放。

  • 请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放;
  • 循环等待条件:指在发生死锁时,必然存在一个线程请求资源的环形链,即线程集合 {T0,T1,T2,…Tn}中的 T0 正在等待一个 T1 占用的资源,T1 正在等待 T2 占用的资源,以此类推,Tn 正在等待己被 T0 占用的资源。

 

死锁的实现

场景设计

  • 创建 2 个线程,线程名分别为 threadA 和 threadB;
  • 创建两个资源, 使用 new Object () 创建即可,分别命名为 resourceA 和 resourceB;
  • threadA 持有 resourceA 并申请资源 resourceB;
  • threadB 持有 resourceB 并申请资源 resourceA ;
  • 为了确保发生死锁现象,请使用 sleep 方法创造该场景;
  • 执行代码,看是否会发生死锁。

结果如下图所示,线程A获取A资源,线程B获取B资源。然后线程A由想要获取B资源,线程B又想获取A资源,他们谁也没有主动释放。

 代码讲解: 

  • 从代码中来看,我们首先创建了两个资源 resourceA 和 resourceB;
  • 然后创建了两条线程 threadA 和 threadB。threadA 首先获取了 resourceA ,获取的方式是代码 synchronized (resourceA) ,然后沉睡 1000 毫秒;
  • 在 threadA 沉睡过程中, threadB 获取了 resourceB,然后使自己沉睡 1000 毫秒;
  • 当两个线程都苏醒时,此时可以确定 threadA 获取了 resourceA,threadB 获取了 resourceB,这就达到了我们做的第一步,线程分别持有自己的资源;
  • 那么第二步就是开始申请资源,threadA 申请资源 resourceB,threadB 申请资源 resourceA 无奈 resourceA 和 resourceB 都被各自线程持有,两个线程均无法申请成功,最终达成死锁状态。
package jvm.juc;

public class DeadLock {
    private static Object resoureceA = new Object();
    private static Object resoureceB = new Object();
    public static void main(String[] args) {
        Thread threadA = new Thread(()->{
            System.out.println("线程A-准备获得A资源....");
            synchronized (resoureceA) {
                System.out.println("线程A-已经获得A资源");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程A-准备获得B资源");
                synchronized (resoureceB) {
                    System.out.println("线程A-已经获得B资源");
                }
            }
        });
        threadA.setName("Thread-A");
        Thread threadB = new Thread(()->{
            System.out.println("线程B-准备获得B资源....");
            synchronized (resoureceB) {
                System.out.println("线程B-已经获得B资源");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程B-准备获得A资源");
                synchronized (resoureceA) {
                    System.out.println("线程B-已经获得A资源");
                }
            }
        });
        threadA.setName("Thread-B");

        threadA.start();
        threadB.start();

    }
}

如何避免线程死锁

要想避免死锁,只需要破坏掉至少一个构造死锁的必要条件即可,学过操作系统的读者应该都知道,目前只有请求并持有和环路等待条件是可以被破坏的。

我们,不让他们循环持有相互的资源就行。

package jvm.juc;

public class DeadLock {
    private static Object resoureceA = new Object();
    private static Object resoureceB = new Object();
    public static void main(String[] args) {
        Thread threadA = new Thread(()->{
            synchronized (resoureceA) {
                System.out.println("线程A-已经获得A资源");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (resoureceB) {
                    System.out.println("线程A-已经获得B资源");
                }
            }
        });
        threadA.setName("Thread-A");
        Thread threadB = new Thread(()->{
            synchronized (resoureceA) {
                System.out.println("线程B-已经获得A资源");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (resoureceB) {
                    System.out.println("线程B-已经获得B资源");
                }
            }
        });
        threadA.setName("Thread-B");

        threadA.start();
        threadB.start();

    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/37266.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

javacc之路0--- 安装与使用

官网: https://javacc.github.io/javacc/ https://github.com/javacc/javacc#getting-started 安装 下载解压 执行: mvn package 将 javacc-7.0.10.jar 重命名为 javacc.jar 并将目录下的scripts文件夹加入到环境变量中。 执行javacc命令验证是否成功…

【Qt 按钮】QPushButton所有函数和样式

【Qt 按钮】QPushButton所有函数和样式一、QSS语句 (界面样式大全)二、 构造函数三、Geometry (获取属性)四、 QFont五、setFont六、text七、setText八、move九、resize十、adjustSize[按钮自动适应文本大小]十一、setFocus十二、…

【附源码】计算机毕业设计JAVA预约健身私教网站

【附源码】计算机毕业设计JAVA预约健身私教网站 目运行 环境项配置: Jdk1.8 Tomcat8.5 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: JAVA my…

开发人员为什么要写测试用例?

作为一名开发人员,你可能会发现周围的开发并不太喜欢写测试用例,甚至有些同学根本不写测试用例,认为写测试用例完全是浪费时间,或者是测试用例只是测试的事情。 在开发过程中,往往都是呼啦啦的写完代码,然后…

【负荷预测、电价预测】基于神经网络的负荷预测和价格预测附Matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。 🍎个人主页:Matlab科研工作室 🍊个人信条:格物致知。 更多Matlab仿真内容点击👇 智能优化算法 …

别人熬夜看世界杯 我熬夜改代码 你满意了

2022年卡塔尔世界杯正如火如荼地进行着, 一边是热火朝天的比赛,一边是让人惊掉下巴的爆冷结局, 但正因为这些不确定因素,反倒让世界杯增添了几分魅力和乐趣! 小编在看球赛的过程中,不禁起了联想&#xff…

Django+Vue中文件的上传和下载

场景:上传一个源数据Excel文件,然后根据数据处理生成另外一个Excel文件并支持下载 Django: 1.首先在Django的settings.py文件中增加配置 MEDIA_URL /media/ MEDIA_ROOT os.path.join(BASE_DIR, media)2.项目的urls.py中增加 url(r^medi…

环境主题静态HTML网页作业作品 大学生环保网页设计制作成品 简单DIV CSS布局网站

🎀 精彩专栏推荐👇🏻👇🏻👇🏻 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业…

百度智能云与实体经济“双向奔赴”: 一场1+1>2的双赢

实体经济,已经成为检验科技企业潜力的试金石。 在最近的财报季中,各家大厂的财报里“实体经济”都是关键字眼,已经成为各家心照不宣的共同目的地。 当然,条条大路通罗马。每一家的战略思路和打法都不一样。11月22日,…

Centos7下新硬盘的挂载操作

1、查看当前硬盘使用情况 df -h 2、 查看磁盘分配情况 fdisk -l 如图所示 vdb 磁盘 还未被使用,现在开始分配。 3、 磁盘分配 使用m查看详细命令,n添加一块新分区,默认最多只能有四个主分区,但可以通过设置将第…

Web前端开发技术课程大作业,期末考试HTML+CSS+JavaScript电竞游戏介绍网站

🎉精彩专栏推荐👇🏻👇🏻👇🏻 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业…

SpringCloudAlibaba全网最全讲解5️⃣之Feign(建议收藏)

🌈专栏简介 感谢阅读,希望能对你有所帮助,博文若有瑕疵请在评论区留言或在主页个人介绍中添加我私聊我,感谢每一位小伙伴不吝赐教。我是XiaoLin,既会写bug也会唱rap的男孩,这个专栏主要是介绍目前微服务最主流的解决方…

4 款适用于 Windows 的最佳免费 GIS 软件

GIS 代表地理信息系统,用于分析、存储、操作和可视化地图上的地理信息。GIS是一种应用广泛的软件,在农业、天文、考古、建筑、银行、航空等各个领域都有应用。开始这些项目,需要 shapefile。一些网站提供不同国家的免费 shapefile。下载免费 …

软件测试入门+面试点

前言:大约是2022年11月18日,我想学习软件测试,在此之前我是Java路线的,这不大环境的竞争激烈在加上自身的能力分析,我认为测试可能是我找工作路上的救赎之光,又恰逢这个时间点,留给我的时间不多…

python实现综合评价模型TOPSIS

原文:https://mp.weixin.qq.com/s/J9fZQ8T9TR1Ed7taPGYYjw 1 TOPSIS简介 有关综合评价的方法有多种, 根据赋权方法不同主要有两类:一类是主观赋值法, 如指数法、层次分析法、模糊综合评价法等;另一类是客观赋值法&am…

Ceph集群部署

目录 一、环境准备 1、准备4台centos服务器 2、配置ceph源 3、配置主机名解析和SSH互信 4、NTP时间同步 二、ceph集群部署 1、安装ceph组件 2、部署MON集群 3、部署OSD集群 一、环境准备 1、准备4台centos服务器 主机主机名IP备注客户端client192.168.2.10关闭selin…

中国传统节日春节网页HTML代码 春节大学生网页设计制作成品下载 学生网页课程设计期末作业下载 DW春节节日网页作业代码下载

🎉精彩专栏推荐 💭文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业: 【📚毕设项目精品实战案例 (10…

常用眼底图像数据集简介及下载--糖尿病视网膜病变(Eyepacs,APTOS2019,Messdior,Messdior-2,STARE数据集)

一、糖尿病视网膜病变图像介绍 1.微动脉瘤通常出现在病变早期,它是由于眼部毛细血管缺氧导致血管壁变薄,从而在视网膜上呈现出深红色的点状物 2.出血点一般出现在血管附近,它是由于血管阻塞导致血液渗出形成的,呈现暗斑状 3.软性和…

包含min函数的栈、栈的压入弹出序列、从上往下打印二叉树、二叉搜索树的后序遍历序列

文章目录1、包含min函数的栈2、栈的压入弹出序列3、从上往下打印二叉树4、二叉搜索树的后序遍历序列1、包含min函数的栈 本题考点: 栈的规则性设计 牛客链接 题目描述: 定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的 m…

python之函数返回值传参Lambda表达式

目录 一、函数 函数与方法的区别 二、 函数返回值 三、函数传参 四、lambda表达式 一、函数 函数与方法的区别 直接调用的是函数 通过对象点出来的是方法 print("hello") a [2, 1, 3] # 对象 a.sort() print(a) 二、 函数返回值 # 返回值 def sum(*ar…