当前位置: 首页 > news >正文

Java EE - 常见的死锁和解决方法

目录

  • 1.可重入锁
  • 2.两个线程两把锁
  • 3.哲学家就餐
  • 4.死锁形成的原因

1.可重入锁

一个线程为保证在并发过程中是线程安全的,就可以考虑加锁来规避线程安全问题,当一个线程需要频繁使用时,就需要多次加锁,如果对同一个线程多次加锁,就可能引起死锁。

例如:现在有一个线程需要完成加法函数的使用,在加锁后完成加法函数的调用;完成加法函数后还需要进行修改操作,在线程内部对修改的操作再次加锁,保证修改操作的安全。

class Test{//锁对象static Object locker = new Object();//求和static int sum;public static void main(String[] args) throws InterruptedException {//定义两个操作数int a = 1;int b = 2;//创建一个线程Thread thread = new Thread(() -> {synchronized (locker){//第一次加锁sum = a + b;//计算加法//此时修改sum变量synchronized (locker){//第二次加锁sum -= (a + b);}}});//开启线程thread.start();thread.join();System.out.println("sum = " + sum);}
}

线程thread在进行加法操作时就第一次加锁,锁对象是定义的locker,第二次修改操作加锁的对象也是locker,同一线程使用同一个锁对象加了两次锁,如果按照锁的性质,锁是互斥的,一个线程获取到锁时,另一个线程需要获取到这个锁就需要进入阻塞等待,所以程序运行后线程thread应该会进入阻塞等待。

执行程序的结果:正常输出sum = 0;

在这里插入图片描述
在程序运行后可以正常的输出结果,线程thread并没有进入阻塞等待。

为什么对同一个线程重复加锁不会形成死锁呢?

在Java中引入了可重入锁,对于同一个线程使用同一个锁对象多次加锁,并不会真正形成死锁,而是在加锁前进行检查,发现锁对象已经对线程加锁,在程序运行前会优化为不加锁,即加锁操作只针对第一次,后续加锁操作会被当作不加锁。

如何设计一个可重入锁呢?
1)记录第一次加锁的对象和线程;
2)每一次加锁前都进行锁对象和线程加锁情况的检查。

2.两个线程两把锁

创建两个线程t1和t2,定义两个锁对象locker1和locker2,两个线程并发执行,t1线程先获取锁对象locker1,t2线程获取锁对象locker2,在保持获取锁的基础上,线程t1请求获取锁对象locker2,线程t2请求获取锁对象locker1,由于此时锁的获取需要解锁后才可申请,两个线程同时请求对方的锁,此时的两个线程就都进入阻塞等待,形成死锁。

在这里插入图片描述

class Test1{//锁对象static Object locker1 = new Object();static Object locker2 = new Object();public static void main(String[] args) {//线程t1Thread t1 = new Thread(() -> {synchronized (locker1){//确保t2线程可以获取到locker2try {Thread.sleep(1);} catch (InterruptedException e) {
//                    throw new RuntimeException(e);}synchronized (locker2){System.out.println("t1获取到locker1和locker2");}}});//线程t2Thread t2 = new Thread(() -> {synchronized (locker2){//加锁:locker2synchronized (locker1){//加锁:locker1System.out.println("t2获取到locker1和locker2");}}});//开启线程t1.start();t2.start();}
}

以上程序并发执行会导致t1线程阻塞等待获取锁对象locker2,t2线程阻塞等待获取锁对象locker1,形成死锁,在默认路劲C:\Program Files\Java\jdk-17\bin找到jconsole打开,发现t1(Thread-0)线程被t2(Thread-1)线程阻塞,t2(Thread-1)线程被t1(Thread-0)线程阻塞。

在这里插入图片描述
在这里插入图片描述

需要解决此类死锁可以将线程改为串行执行,程序就可以正常运行,先开启线程t1,再开启线程t2,开启后调用join方法使main线程进入阻塞等待。

class Test1{//锁对象static Object locker1 = new Object();static Object locker2 = new Object();public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {synchronized (locker1){//确保t2线程可以获取到locker2try {Thread.sleep(1);} catch (InterruptedException e) {
//                    throw new RuntimeException(e);}synchronized (locker2){System.out.println("t1获取到locker1和locker2");}}});Thread t2 = new Thread(() -> {synchronized (locker2){synchronized (locker1){System.out.println("t2获取到locker1和locker2");}}});t1.start();t1.join();//main线程阻塞等待,此时还未开启线程t2t2.start();//开启线程t2t2.join();//main线程阻塞等待}
}

在这里插入图片描述

3.哲学家就餐

现在有n个哲学家围在同一桌就餐,但是在餐桌上只提供n根筷子,筷子放在哲学家之间,哲学家需要就餐需要两根筷子,但是左右的筷子除了本人能拿到,相邻的哲学家也可拿到,如何解决哲学加就餐问题?

在这里插入图片描述

在极端的情况下可能每一个哲学家都先拿起左手边的一个筷子,此时每一个哲学家都可以拿到一根筷子,为了就餐,哲学家需要再拿起一根筷子,需要从右手边拿,如果此时每个哲学家都想就餐,每一个哲学家就都拿不到第二根筷子,所有哲学家就都无法就餐。

以上情形如果是在多线程的情况下执行,就会引发多个线程进行阻塞等待,可能所有线程都无法完成任务,还会消耗资源,形成死锁。

解决以上由于锁的竞争导致的死锁,可以采取按照同样的加锁顺序来获取锁。

使用上述的t1和t2线程,locker1和locker2线程的例子,线程t1和t2同时获取锁对象locker1,在获取锁对象的基础上,获取锁对象locker2。

class Test1{//锁对象static Object locker1 = new Object();static Object locker2 = new Object();public static void main(String[] args){//同时获取锁对象locker1,再获取锁对象locker2Thread t1 = new Thread(() -> {synchronized (locker1){synchronized (locker2){System.out.println("t1获取到locker1和locker2");}}});Thread t2 = new Thread(() -> {synchronized (locker1){synchronized (locker2){System.out.println("t2获取到locker1和locker2");}}});t1.start();//开启线程t1t2.start();//开启线程t2}
}

在这里插入图片描述

4.死锁形成的原因

1)锁是互斥的;一个线程获取到一把锁,其它线程申请获取到这把锁就需要阻塞等待;

2)锁是不可争夺的;线程获取到锁的资源,在解锁前,其它线程是不可享受到这把锁的资源;

3)请求和保持;线程获取到锁资源的基础上,请求获取其它线程获取的锁;

4)循环等待;多个线程申请锁的过程中,阻塞的线程间形成环。
在这里插入图片描述

以上就是本篇文章的所有内容,如果有任何疑问,欢迎评论区讨论留言,我们下一篇文章再见!

http://www.dtcms.com/a/617573.html

相关文章:

  • transformer 教程(一) qkv矩阵介绍以及为什么除以根号d推导
  • 网络网站开发江苏电信网站备案
  • 树莓派 5 上 Ubuntu 24.04 LTS 自带 RDP 远程桌面重启密码就变
  • 算法---贪心算法(Greedy Algorithm)
  • TDengine 字符串函数 REGEXP_IN_SET 用户手册
  • 佛山市外贸网站建设公司因网站开发需要
  • 神经网络组植物分类学习规划与本周进展综述15
  • 做律师事务所网站牡丹江住房和城乡建设厅网站
  • 上海崇明林业建设有限公司 网站建设 市民中心网站
  • 在UEC++中使用什么方式返回像 FTransform 这种类型的值
  • GPT‑OSS‑20B MoE 在昇腾 NPU 上的部署与性能实测:开发者视角的多精度推理优化实践
  • 后端服务弹性伸缩实践实践:让系统更高效、稳定
  • 网站的比较做网站哪家便宜
  • 寻找昆明网站建设手机网站 跳转
  • 建站网址大全56做视频网站
  • jsp网站开发教程响应式布局是什么意思
  • dede中英文企业网站建筑方案设计案例
  • 常见网站颜色搭配技术导航源码
  • 简单响应式网站设计代码翻书效果的网站
  • 网站seo方案建议电影网站怎么做优化
  • 网站开发外包不给ftp外贸推广方式
  • 百度 网站 说明qq是哪家公司运营的
  • 门头设计效果图网站西安学网站开发哪边好
  • 公司网站模板源代码沈阳百度seo
  • 房产网站怎么做才能吸引人怎样做自己网站
  • 不错宁波seo公司网站优化 推广
  • 好的网站首页建设公司网站托管费用 优帮云
  • 企业网站策划方案模板品牌设计网站建设
  • 微科技h5制作网站模板下载网站建设龙卡要审批多久时间
  • 响应式 网站 设计软件苏州网站开发网站开发费用