Java死锁的原因及解决方法

news2024/11/14 3:52:56

        要想知道死锁出现的原因和解决方法,首先得知道什么是死锁,死锁是两个或两个以上的运算单元(进程、线程或协程),互相持有对方所需的资源,导致它们都无法向前推进,从而导致永久阻塞的问题。从字面意义上去理解并不太好理解,用实际情况来讨论可以更好的理解。

public synchronized void print(){
    synchronized (this){
        System.out.println("hahha");
    }
}

当代码执行到这个方法,会对该方法进行加锁,避免其他的线程加入进来干扰代码的运行,但是当代码执行到代码块里面时,发现这里又需要加锁,而且加的还是同一个锁(当synchronized对普通方法进行加锁时,相当于对this进行加锁,当对静态方法进行加锁时,相当于对类对象进行加锁),那么就会发生冲突,产生锁竞争。因为该this已被加锁,要想执行下去,就必须对this进行解锁,而要进行解锁,就得代码继续执行下去,这就导致代码逻辑产生矛盾,使得它们各自都无法向前推进,从而导致永久堵塞,这就产生了死锁,也是死锁的一种体现形式,这种死锁一眼就能看出,而方法的调用而导致出现的死锁就让人防不胜防。

public synchronized void func1(){
    func2();
}
public void func2(){
    synchronized (this){};
}

或许现在可以一眼就能发现死锁,但是当调用的方法更多时,还能保证不会出现死锁吗?不过这种情况是不科学的,因为此时进行两次加锁的是同一个线程,为什么说它是不科学的呢?就像你有了一把钥匙,这把钥匙打开了大门,但此时里面还有一扇挂着相同的锁的大门,你就不能用这把钥匙打开下一扇大门了吗?当然可以,因此,当第二次尝试加锁时,该线程已经有了这个锁的权限了,这个时候不应该加锁失败,也不应该阻塞等待。为了应对这种情况,Java也就产生了两种锁,一种是可重入锁,一种是不可重入锁。不可重入锁就是锁不会被保存,当它被某个线程给加了锁后,只要它处于加锁状态时,当它再次收到加锁的请求后,就会拒绝当前加锁,即使是该加锁的请求是对它已经加锁了的线程。而可重入锁则是会保存当前锁,当它被某个线程给加了锁后,当它再次收到加锁的请求,则会对比当前要进行加锁操作的线程和已经加了锁的线程是否一样,这样就可以灵活判断了,如果相同就不进行加锁且直接执行,如果不相同,那就得堵塞了,而synchronized就是可重入锁,因此上述代码不会出现这种情况。如果出现这种情况的话,什么时候进行解锁呢?

public void func2(){
    synchronized (this){
        synchronized (this){
            synchronized (this){
                ……;
            }
        }
    };
}

这很容易想到,当然是要出了最外面的synchronized才能进行解锁,不过我们虽然知道要到最外面的synchronized才解锁,但是jvm怎么知道什么时候才到了最外面的synchronized呢?其实很简单,增加一个计数器就行,只需要在每次进行加锁操作时,让计数器进行加一,每一个出锁操作就让计数器减一,这不就很好的解决了吗?到这死锁的一种典型情况就结束了,那就是一个线程一把锁,但是该锁是不可重入锁,并且一个线程对该锁进行了多次加锁,导致死锁的出现。

        现在就来谈谈死锁的第二种情况:两个线程两把锁,但是这两个线程同时获取对方的锁,这也将导致死锁的发生。

public void func3(){
    synchronized (locker1){
        synchronized (locker2){
            ;
        }
    }
}
public void func4(){
    synchronized (locker2){
        synchronized (locker1){
            ;
        }
    }
}

因为两个线程同时分别调用这两个方法,导致这两把锁同时被加锁,当它们又要再次获取另一把锁时,就会因为该锁被占用而导致线程进入阻塞状态,直到要获取的锁被释放,而因为这两个线程要想进行下去,就必须获取锁,要想获取锁就要进行下去,就如钥匙放在家里,要想进去家里,就得拿钥匙,要想拿钥匙,就得进家里,因而导致死锁的出现。

        而死锁的出现还有第三种情况,那就是N个线程M把锁,比如将一堆人围成一圈吃饭,在每个人的间隔中放一根筷子,而想要吃饭就必须要那两根,并且他们中的人什么时候开始吃饭都是不确定的,而当他们开始吃饭时,拿到筷子,除非吃完否则绝不放下,哪怕只拿到一根,而没拿到的就只能等待有筷子的时候。倘如有些人拿到了两根筷子,那么势必有人只有一根筷子甚至没有筷子,这就造成堵塞,将一跟筷子看成是一把锁,只有获得两把锁的线程才能顺利进行下去,那么那些只有一把锁甚至没有锁的线程就会堵塞,倘如有两个线程出现死锁出现的第二种情况,就会产生死锁,这就是N个线程M把锁出现死锁的情况。

        根据死锁出现的三种情况,可以发现其中有着共同之处,可以认为是死锁出现的必要原因。

1)互斥使用:当一个线程获取到一把锁后,其他线程在该线程释放该锁时,是不能获取该锁。

2)不可抢占:锁只能被所有者主动释放,而不能被其他线程给强行释放并直接加锁。

3)请求和保持:当线程获取一把锁后,在获取另一把锁时,会保持对第一把锁的的获取状态。

4)循环等待:t1想要获取locker2,就要等待t2释放locker2, t2想要获取locker1就要等待t1释放locker1,因此它们就得循环等待。

要想出现死锁,上述原因缺一不可。既然知道了死锁出现的原因,那么只需要破坏其中一个原因就可以避免死锁的出现了。因为原因1和原因2是锁的特性,无法修改,那么只能对原因3和4下手了,原因3和原因4取决与代码的结构。不过有时原因3会影响到需求,因此大部分时候对原因4进行改动。比如对所有的锁进行编号,并且规定必须得先对小的加锁,才能对大的加锁,只要代码严格按照该方法进行加锁,那么就可以规避循环等待。

private void func5(){
    synchronized (locker1){
        synchronized (locker3){
            synchronized (locker5){
                ;
            }
        }
    }
}

private void func6(){
    synchronized (locker2){
        synchronized (locker3){
            synchronized (locker4){
                ;
            }
        }
    }
}

到此死锁就此结束。

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

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

相关文章

Spine2D骨骼动画播放器 - 微信小程序版

Spine2D骨骼动画播放器 - 微信小程序版 简介平台支持 界面预览使用说明演示视频 版本笨笨的小目标(废话)参考资料测试文件百度盘分享 相关文档 简介 本播放器是SpinePlayer的微信小程序版。由于官方并没有提供现成的运行库,只能自己改造。 设…

如何应用运营商大数据精准营销?

如何应用运营商大数据精准营销? 越来越多的企业逐渐觉察到运营商大数据所带来的商业价值,精准营销也被他们用的越来越娴熟。那么,企业的大数据精准营销该如何应用呢?想必是很多资源有限的中小型公司最想了解的。 一 数据驱动运营…

springCloud-LoadBalancer负载均衡

接上个博客springcloud-Eureka。 Eureka主要是如何通过eureka服务器进行服务注册与发现,也有简单的负载均衡,实际上它其中的负载均衡就是靠LoadBalancer实现的。 2020年前SpringCloud是采用Ribbon作为负载均衡实现,但是在2020后采用了LoadBal…

部署elasticsearch集群

创建es集群 编写一个docker-compose.yaml文件,内容如下 version: 2.2 services:es01:image: elasticsearch:7.12.1container_name: es01environment:- node.namees01- cluster.namees-docker-cluster- discovery.seed_hostses02,es03- cluster.initial_master_nod…

大数据安全 | (一)介绍

目录 📚大数据安全 🐇大数据安全内涵 🐇大数据安全威胁 🐇保障大数据安全 ⭐️采集环节安全技术 ⭐️存储环节安全技术 ⭐️挖掘环节安全技术 ⭐️发布环节安全技术 🐇大数据用于安全 📚隐私及其…

CSS笔记(黑马程序员pink老师前端)选择器,字体,文本属性,Emmet语法,元素显示模式,CSS背景

选择器 选择器分为基础选择器和复合选择器两大类。 基础选择器 包括:标签选择器、类选择器、id选择器和通配符选择器。 /*标签选择器 */p {color: red;}/*类选择器 */.classname {color: yellow;}/*id选择器 */#idname {color: blue;}/*通配符选择器,选择页面所有的…

核心实验10_hybrid(实现access和trunk功能)_ENSP

项目场景: 核心实验10_hybrid(实现access和trunk功能)_ENSP 用hybrid的属性 是否打标签来达到替代使用access或trunk接口的目的 实搭拓扑图: 具体操作: SW1: [sw1]vlan 10 [sw1-vlan10]int g0/0/1 [sw1-GigabitEther…

ChatGPT 超有用提示词 练习雅思口语

目录 Prompts 🔻作为一个英语口语老师和提高英语口语 方法1:口语简单练习 方法2:角色扮演练习口语 作为一个英语翻译/英语作文优化师/稿件校对 作为一个”职位”面试官 学习英文单词 演员 苏菲 玛索 阿尔弗雷多詹姆斯帕西诺 要孝顺…

LeetCode 1113.报告的记录

数据准备 Create table If Not Exists Actions (user_id int, post_id int, action_date date, action ENUM(view, like, reaction, comment, report, share), extra varchar(10)); Truncate table Actions; insert into Actions (user_id, post_id, action_date, action, ext…

Python用GAN生成对抗性神经网络判别模型拟合多维数组、分类识别手写数字图像可视化...

全文链接:https://tecdat.cn/?p33566 生成对抗网络(GAN)是一种神经网络,可以生成类似于人类产生的材料,如图像、音乐、语音或文本(点击文末“阅读原文”获取完整代码数据)。 相关视频 最近我们…

Unity 之 Material (材质)渲染3D对象的重要组件

文章目录 介绍一些代码例子 介绍 在Unity中,Material(材质)是一种用于渲染3D对象的重要组件。Material定义了对象的外观,包括其颜色、纹理、光照属性和反射等。以下是关于Material的详细介绍: 创建Material&#xff1…

IDEA报错:No valid Maven installation found

当我想要用maven进行clean的时候,发现报了这个错误,idea的event logs记录为 网上又说可能是因为你的maven环境没有配置好,我对我的maven进行了检查,发现是没有问题的,在这里提醒大家,如果你以前的项目maven…

Java入门第三季

一、异常与异常处理 1. 异常简介 在Java中,**异常是程序在执行过程中出现的问题或意外情况,导致程序无法按照预期的流程进行。**异常处理是Java中用于处理程序中出现的异常的一种机制。 Java中的异常可以分为两大类:受检查的异常&#xff…

Linux——zabbix

简介 官网:https://www.zabbix.com/ Zabbix 是一个开源的网络监控和警报解决方案。它允许管理员监控网络中的各种设备、服务器和应用程序,并收集有关它们性能和状态的数据。Zabbix 提供了一个集中化的管理界面,通过各种监控方式&#xff08…

大模型技术实践(三)|用LangChain和Llama 2打造心灵疗愈机器人

上期文章我们实现了Llama 2-chat-7B模型的云端部署和推理,本期文章我们将用“LangChainLlama 2”的架构打造一个定制化的心灵疗愈机器人。有相关知识背景的读者可以直接阅读「实战」部分。 01 背景 1.1 微调 vs. 知识库 由于大模型在垂直行业领域的问答效果仍有待提…

基于Simulink的用于电力系统动态分析

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

zemax双透镜公差分析

公差分析,就是在设计了一个理想的系统后,想看看实际生产过程中如果产生公差(误差),系统会坏到什么程度,也就是光学性能受到多大影响。 先设计出双透镜: 在zemax中找到公差选项卡,准…

算法专题:前缀和

文章目录 Acwing:前缀和示例2845.统计趣味子数组的数目思路容易理解的写法:前缀和两层循环存在问题:超时 优化写法:两数之和思路,转换为哈希表 前缀和,就是求数组中某一段的所有元素的和。 求子数组中某一…

输运方程的推导

1 概述 对于流场中守恒的物理量,均可采用输运方程(transport equation)进行描述其随时间变化和在空间的分布规律。输运方程的通用形式为: 输运方程描述了流动过程中的物理量守恒,其包括瞬态(transient&…

FPGA实战小项目2

基于FPGA的贪吃蛇游戏 基于FPGA的贪吃蛇游戏 基于fpga的数字密码锁ego1 基于fpga的数字密码锁ego1 基于fpga的数字时钟 basys3 基于fpga的数字时钟 basys3