Java多线程(八)

news2024/11/28 16:26:41

目录

一、产生死锁的情况

1.1 一个线程多把锁

1.1.1 Java中可重入锁的实现机制

1.2 两个线程两把锁

1.3 N个线程M把锁

二、解决死锁的方案

2.1 死锁的必要条件

2.2 破除循环等待


一、产生死锁的情况

死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。

1.1 一个线程多把锁

在一个线程多把锁的情况下,首先成功地拿到第一个锁,但是当遇到第二个锁的时候就会产生阻塞等待,解决这个阻塞等待就需要先进行锁释放,而锁释放就需要线程进入。就好比是想打开房门,但是房门钥匙在房间里面。这个过程就产生了一直等待的结果变成死锁。

synchronized (locker) {
    synchronized (locker) {
        System.out.println("一个线程多把锁");
    }
}

但是在Java中这个问题是不会产生死锁的,因为synchronized是可重入锁,可以灵活的判定这个锁是不是已经加过了,从而不会产生死锁。但是在c++的锁就是不可重入锁,这种代码的写法就是会产生死锁。

1.1.1 Java中可重入锁的实现机制

由于Java中的synchronized锁是可重入锁,在一个线程多把锁的情况下也不会重复加锁,但是这就引出了另一个问题,在中间加锁的过程中锁是不应该被释放的,那jvm又如何知道什么时候去释放锁呢?

sychronized的解决方式是利用一个计数器,没当遇到一个加锁操作的时候就+1,遇到一个解锁操作的时候就-1,当计数器为0的时候才真正的去释放锁。

1.2 两个线程两把锁

两个线程分别获取一把锁,然后再去获取对方的锁,这样就会产生死锁。

public class Demo21 {
    public static void main(String[] args) {
        Object locker1 = new Object();
        Object locker2 = new Object();
        Thread t1 = new Thread(()->{
           synchronized (locker1) {
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
               synchronized (locker2) {
                   System.out.println("对locker2对象加锁");
               }
           }
        });
        Thread t2 = new Thread(()->{
            synchronized (locker2) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized (locker1) {
                    System.out.println("对locker1对象加锁");
                }
            }
        });
        t1.start();
        t2.start();
    }
}

这里程序运行出来就会产生死锁,当这里的locker1想要去获取locker2时,由于locker2已经加锁了就需要等待locker2释放锁,但是locker2释放锁又需要去获取locker1的锁,而locker1也是处于加锁的一个状态。就好比是回家发现房门钥匙在车里,然后车钥匙在家里。这样就产生了一直等待变成死锁。

1.3 N个线程M把锁

“哲学家就餐问题”,这里引入5个哲学家5根筷子来对应5个线程5把锁的情况。每个哲学家要就餐就需要先拿起左手筷子再拿右手的筷子,而其他的哲学家想就餐就只能等待其中的一根筷子放下。 

在一般的情况下是不会产生冲突的,但是当5个哲学家同时拿起左手的筷子时,就会产生一直等待的情况。这样就会产生死锁。

二、解决死锁的方案

2.1 死锁的必要条件

1.互斥使用:一个线程获取到这个锁,别的线程不能获取这个锁

2.不可抢占:锁只能由前一个持有者主动释放,不能被其他线程抢走

3.请求和保持:一个线程获取多把锁的时候,会对前一个锁保持获取状态

4.循环等待:要获取一个被持有的锁时,就需要一直等待,直到持有者释放锁

了解到了死锁的必要条件,只要破除其中一个就能够解决死锁问题。但是在前两个条件中,既是死锁的必要条件也是锁的特性,所以这两个条件无法破除。所以解决死锁问题只能从3和4下手,但是如果要解决3的条件就需要更改大部分代码的逻辑,成本是很高的。所以为了解决死锁问题,最好的解决办法就是破除循环等待的条件。

2.2 破除循环等待

银行家算法可以解决死锁问题,但是较为复杂,在解决死锁问题的过程中可能会引发更加复杂的问题,所以一般解决死锁问题,不建议使用银行家算法。

这里引入一种简洁有效的方法去解决死锁问题:针对锁进行编号,并且规定加锁顺序。例如,每个线程想要获取多把锁就需要先获取编号最小的锁。

同样的将两个线程两把锁的情况按照这种情况进行修改就会解决死锁问题

public class Demo21 {
    public static void main(String[] args) {
        Object locker1 = new Object();
        Object locker2 = new Object();
        Thread t1 = new Thread(()->{
           synchronized (locker1) {
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
               synchronized (locker2) {
                   System.out.println("对locker2对象加锁");
               }
           }
        });
        Thread t2 = new Thread(()->{
            synchronized (locker1) {//先获取编号顺序小的锁
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized (locker2) {
                    System.out.println("对locker1对象加锁");
                }
            }
        });
        t1.start();
        t2.start();
    }
}

再次运行发现已经解决死锁问题了

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

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

相关文章

开放式耳机很吵吗?开放式耳机推荐

​一般情况下,开放式耳机是不会吵到别人,开放设计,使声音不会被完全封闭在耳朵里,而是向四周扩散。与传统的封闭式耳机相比,开放式耳机以其出色的音质和宽广的音场而备受追捧。选择一款适合自己的开放式耳机无疑是至关…

金蝶云星空对接打通管易云分布式调入单查询接口与其他入库单新增完结接口接口

金蝶云星空对接打通管易云分布式调入单查询接口与其他入库单新增完结接口接口 源系统:金蝶云星空 金蝶K/3Cloud在总结百万家客户管理最佳实践的基础上,提供了标准的管理模式;通过标准的业务架构:多会计准则、多币别、多地点、多组织、多税制应…

Three.js WebXR沉浸式渲染简明教程

在前面文章中,我们了解了 VR 概念以及它们如何在 WebXR 中映射。 这使你可以考虑想要为用户提供的体验。 在本文中,我们将介绍如何将 WebXR 与 Three.JS 结合使用来创建针对大型异构用户群的沉浸式体验。 警告:WebXR API 仍在完善中&#xf…

C++学习笔记总结练习:数值方法

数值方法 1.1 随机数 头文件 #include<random>随机数概述 随机数分布。随机数的分布方式distribution 随机数引擎。产生随机数engin。随机性的源头 随机数生成器。由一个随机数引擎和一个随机数分布&#xff0c;组合成一个随机数生成器。 随机数引擎的操作 编译器…

关于@JSONField的使用

1.此注解来自jar包com.alibaba.fastjson 今天分享一个有意思的事情。这个注解作用与类的属性上&#xff0c;如下&#xff1a; ApiModelProperty(value"开始时间,格式:yyyy-MM-dd",required true) JSONField(name"start_date",ordinal 1) private String…

扫盲!PRINCE2认证6大常见问题集锦!

一&#xff0c;什么是PRINCE2认证&#xff1f; PRINCE2是PRoject IN Controlled Environment&#xff08;受控环境下的项目管理&#xff09;的简称&#xff0c;也叫国际项目管理师认证&#xff0c;是英国商务部(OGC)在1996年开始推广世界三大项目管理体系之一。 PRINCE2是一套…

API数据安全风险飙升! 3场景1实践看美创科技API-SMAC有效防护

在某次实战攻防演练中&#xff0c;防守方层层布防&#xff0c;搭建了十分健全的防御体系&#xff0c;本以为万无一失&#xff0c;结果靶标悄无声息被拿下。事后溯源中才发现&#xff0c;一个存在未授权访问的历史API&#xff0c;成为了突破口&#xff0c;敏感信息被红队获取&am…

【LeetCode 75】第二十一题(1207)独一无二的出现次数

目录 题目: 示例: 分析: 代码运行结果: 题目: 示例: 分析: 用两个unordered_map来分别存放每个数字的出现次数和出现的次数这个数,有点绕,比如说有给的数组有两个1,那么第一个map存放的是(1,2),表示1这个数子出现了两次,而第二个map存放的是(2,true),表示有出现次数为2的数…

python——案例11:数值交换

案例11&#xff1a;数值交换xinput(输入一个数值赋值给x&#xff1a;) yinput(输入一个数值赋值给y&#xff1a;)tempx #创建临时变量&#xff0c;以此变量为基础进行逐次交换 xy ytemp print(交换后的X的值是:{}.format(x)) # print(交换后的Y的值是:{}.format(y)) #

java【native关键字】

描述&#xff1a; native只能修饰方法&#xff0c;表示这个方法的方法体代码不是用java语言实现的&#xff0c;而是由c/c语言编写的。但是对于java程序员来说&#xff0c;可以当作java的方法一样正常去调用它&#xff0c;或者子类重写它 语法&#xff1a; 用在方法的返回值类…

Chrome开发者工具探秘:元素面板的神奇魔法与实战解析

作为一名网络爬虫大师&#xff0c;我深知Chrome开发者工具中的元素面板是探索和理解网页结构的重要工具。在本文中&#xff0c;我将详细介绍元素面板的各项功能与使用方法&#xff0c;并通过实际案例&#xff0c;带您领略这个神奇魔法的威力。 元素面板&#xff1a;解读网页的…

本地跑Mapreduce程序的相关配置

本地跑MapReduce程序需要配置的代码 为了在本地运行MapReduce程序&#xff0c;需要加如下的东西 在项目中创建一个如图所示的包&#xff1a;org.apache.hadoop.io.nativeio&#xff0c;并在该包下面创建一个名为&#xff1a;NativeIO的类&#xff08;注意&#xff1a;名字不能…

AD19 基础应用技巧(PCB设置快捷键)

众所周知&#xff0c;学会一个软件的快捷键操作可以大大提高我们的工作效率。 那么&#xff0c;Altium Designer软件如何设置快捷键&#xff1f; 以设置走线/放置过孔为例。 菜单栏 - 【放置】- 然后【Ctrl 鼠标左键 单击过孔】进入【Edit Command】界面。 在快捷方式一栏…

247 个经典实用有趣的 Python 实例附源码

今天给大家整理了 247 个经典实用有趣的 Python 实例&#xff0c;185 页代码齐全可复制 pdf&#xff0c;几乎涵盖了 Python 各个方面的知识点&#xff0c;即可以帮助小白快速全面的学习 Python&#xff0c;也可以让老手通过实战练习来查缺补漏。 福利&#xff1a;文末有chat-g…

用于农业格局分析的新型大型航空影像数据库

第一次农业革命发生在大约12&#xff0c;000年前&#xff0c;当时人类定居并开始种植农作物。从那以后&#xff0c;我们极大地改善了农业的艺术和科学&#xff0c;扩大了规模和产量&#xff0c;并在此过程中塑造了人类文明。一场新的、人工智能驱动的农业革命现在开始了吗&…

《合成孔径雷达成像算法与实现》Figure3.4

代码对补零信号与未补零信号都进行了实现&#xff0c;补零信号更加贴近书中图3.4的样子&#xff1a; clc clear all close all%参数设置 TBP 100; %时间带宽积 T 10e-6; %脉冲持续时间 alpha_os [1.4,1.2,1.0,0…

报考红帽认证难不,红帽认证考试容易吗?

红帽认证是由红帽Linux公司推出的&#xff0c;红帽培训和测试非常注重培养实际的动手实战能力&#xff0c;主要包括RHCSA认证、RHCE认证和RHCA认证&#xff0c;每个等级的认证都是层层递进的。 要想参加RHCA认证就必须通过RHCE认证&#xff0c;且认证证书在有效期内方可参加。 …

【C++进阶之路】异常篇

文章目录 前言一、异常1.简单使用2.注意事项3.异常体系①C标准异常体系②自定义异常体系 4.总结优点缺点 前言 是否知道C语言独特的错误处理方式——返回错误码&#xff0c;我们可以根据错误码来识别错误信息&#xff0c;比如识别了错误码&#xff0c;我们再用strerror函数把错…

信号产生梳状滤波效应的原理和代码演示

声学的梳妆滤波效应&#xff0c;是由于信号沿不同路径传播&#xff0c;时延不同造成的&#xff0c;对吧&#xff1f; 是的&#xff0c;声学的梳妆滤波效应是由于声音信号在传播过程中经历多条不同路径的反射和折射&#xff0c;导致信号到达听者耳朵的时间延迟不同&#xff0c;从…

CentOS7 安装 MongoDB5

MongoDB是一种NoSQL数据库&#xff0c;它存储数据的方式与传统的关系型数据库不同。MongoDB使用文档数据库模型&#xff0c;将数据存储在自包含的、可扩展的BSON文档中。MongoDB具有高可用性、自动分片、动态查询能力、灵活性等优点&#xff0c;适合于许多不同的应用场景。 下…