Java并发编程(十七):死锁

Java并发编程(十七):死锁死锁 1 概述 2 死锁 2 1 死锁的概念 2 2 产生死锁的必要条件 2 3 死锁示例 2 4 预防死锁 2 4 1 控制获取锁的顺序预防死锁 2 4 2 超时机制预防死锁 3 总结大家好 我是欧阳方超 公众号同名

欢迎大家来到IT世界,在知识的湖畔探索吧!

死锁

  • 1 概述
  • 2 死锁
    • 2.1 死锁的概念
    • 2.2 产生死锁的必要条件
    • 2.3 死锁示例
    • 2.4 预防死锁
      • 2.4.1 控制获取锁的顺序预防死锁
      • 2.4.2 超时机制预防死锁
  • 3 总结

大家好,我是欧阳方超,公众号同名。

Java并发编程(十七):死锁

欢迎大家来到IT世界,在知识的湖畔探索吧!

1 概述

工具是中性的,如果使用不当就会导致一定后果,死锁就是这样一个典型的例子。

2 死锁

2.1 死锁的概念

死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象。如果有外部干涉,这些线程将永远等待下去,程序无法正常结束。在Java中,死锁通常发生在多个线程同时访问多个共享资源,并且每个线程都在等待其他线程释放自己所需的资源。

2.2 产生死锁的必要条件

互斥条件:资源不能被共享,一个资源每次只能被一个线程使用。例如,一个文件锁在同一时刻只能被一个线程获取,其他线程如果要获取这个锁,就必须等待。
请求与保持条件:一个线程已经持有至少一个资源,但又提出了新的资源请求,而新资源已被其他线程占有,这个线程会被阻塞,但它不会释放已经持有的资源。
不可剥夺条件:线程已获得的资源,在未使用完之前,不能被其他线程强行剥夺,只能由自己释放。
循环等待条件:存在一组线程,每个线程都在等待下一个线程所占用的资源,形成一个循环等待的链。

2.3 死锁示例

以下是一个简单的Java死锁示例,模拟了两个线程争夺两把锁的情况:

public class DeadLockTest { private static Object lock1 = new Object(); private static Object lock2 = new Object(); public static void main(String[] args) { //创建两个线程对象 DeadLockTest deadLockTest = new DeadLockTest(); Thread thread1 = new Thread(() -> { synchronized (lock1) { System.out.println("hold lock1"); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("waiting lock2 release"); synchronized (lock2) { System.out.println("hold lock1 and lock2"); } } }); Thread thread2 = new Thread(() -> { synchronized (lock2) { System.out.println("hold lock2"); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("waiting lock1 release"); synchronized (lock1) { System.out.println("hold lock1 and lock2"); } } }); thread1.start(); thread2.start(); } } 

欢迎大家来到IT世界,在知识的湖畔探索吧!

上面示例中,定义了两个对象lock1和lock2作为锁,thread1线程先获取lock1锁,然后休眠一段时间(模拟业务操作),接着尝试获取lock2锁,thread2线程先获取lock2锁,然后休眠一段时间,接着尝试获取lock1锁,这样就形成了一个循环等待的情况,thread1等待thread2释放lock2,同时thread2等待thread1释放lock1,导致死锁。

2.4 预防死锁

锁的顺序:确保所有线程以相同的顺序获取锁。
超时机制:使用tryLock方法(在java.util.concurrent.locks.Lock中)来尝试获取锁,并设置超时时间。
减少锁的持有时间:尽可能减少锁的持有时间,以降低发生死锁的可能性。

2.4.1 控制获取锁的顺序预防死锁

可以将上面的程序改造一下,让两个线程thread1和thread2以同样的顺序获取锁,下面是修改后的示例:

欢迎大家来到IT世界,在知识的湖畔探索吧!public class DeadLockTest { private static Object lock1 = new Object(); private static Object lock2 = new Object(); public static void main(String[] args) { //创建两个线程对象 DeadLockTest deadLockTest = new DeadLockTest(); Thread thread1 = new Thread(() -> { synchronized (lock1) { System.out.println("hold lock1"); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("waiting lock2 release"); synchronized (lock2) { System.out.println("hold lock1 and lock2"); } } }); Thread thread2 = new Thread(() -> { synchronized (lock1) { System.out.println("hold lock2"); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("waiting lock1 release"); synchronized (lock2) { System.out.println("hold lock1 and lock2"); } } }); thread1.start(); thread2.start(); } } 

在修改后的代码中,两个线程thread1和thread2都按照先获取lock1,再获取lock2的顺序来访问资源。这样就避免了循环等待的情况,因为不会出现一个线程等待另一个线程释放自己需要的下一个资源的情况。

2.4.2 超时机制预防死锁

另外一种方法是使用java.util.concurrent.locks.ReentrantLock的tryLock()方法来设置获取锁的超时时间。如果在超时时间内无法获取锁,线程可以放弃获取,做一些其他的操作,然后再尝试获取。

import java.util.concurrent.locks.ReentrantLock; public class DeadLockTest { private static ReentrantLock lock1 = new ReentrantLock(); private static ReentrantLock lock2 = new ReentrantLock(); public static void main(String[] args) { Thread thread1 = new Thread(() -> { try { if (lock1.tryLock()) { System.out.println("Thread 1 acquired lock1"); Thread.sleep(300); if (lock2.tryLock()) { System.out.println("Thread1 acquired lock2"); Thread.sleep(300); lock2.unlock(); } else { System.out.println("Thread1 failed to acquire lock2"); } lock1.unlock(); } else { System.out.println("Thread1 failed to acquire lock1"); } } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); } }); Thread thread2 = new Thread(() -> { try { if (lock2.tryLock()) { System.out.println("Thread2 acquired lock2"); Thread.sleep(300); if (lock1.tryLock()) { System.out.println("Thread2 acquired lock1"); Thread.sleep(300); lock1.unlock(); } else { System.out.println("Thread2 failed to acquire lock1"); } lock2.unlock(); } else { System.out.println("Thread2 failed to acquire lock2"); } } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); } }); thread1.start(); thread2.start(); } } 

上面的示例中,首先定义了两个ReentrentLock对象lock1和lock2,每个线程在获取锁时,先使用tryLock()方法尝试获取,如果成功获取到第一个锁(lock1或lock2),线程会休眠一段时间(模拟业务操作),然后再尝试获取第二个锁。如果在规定时间内(tryLock()方法会立即返回,不会一直等待)无法获取第二个锁,线程会释放已经持有的第一个锁,并输出相应的信息,表示无法获取全部需要的锁。这样就避免了线程无限期地等待其他线程释放锁,从而避免了死锁的发生。

3 总结

本文围绕 Java 死锁展开,介绍其概念为多线程争资源致互相等待,阐述产生死锁的互斥、请求与保持、不可剥夺、循环等待四个条件,并给出示例。还详述预防死锁方法,如锁顺序一致、用 tryLock 设置超时机制及减少锁持有时间,且分别展示了相应的代码改造示例以有效避免死锁情况。
我是欧阳方超,把事情做好了自然就有兴趣了,如果你喜欢我的文章,欢迎点赞、转发、评论加关注。我们下次见。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/104132.html

(0)
上一篇 14小时前
下一篇 14小时前

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信