如何避免Java中的死锁?是多线程赛季味道的问题之一,在高级别上提出了更多问题并带有大量后续问题。尽管问题看起来非常基本,但大部分开发人员一旦开始深入研究就会陷入困境。
面试问题从“ 什么是死锁? ” 开始,
答案很简单,当两个或更多线程正在等待对方释放锁定并在无限时间卡住时,情况就称为死锁。它只会发生在多任务的情况下。
其他方法是在运行应用程序时实际上被锁定时尝试进行线程转储时发现它,在Linux中可以通过命令 “kill -3” 执行此操作,这将打印应用程序日志文件中所有线程的状态,并且您可以看到哪个线程被锁定在哪个对象上。
其他的方法是使用 jconsole ,它会告诉你究竟哪些线程被锁定以及哪个对象。
这是我的一个版本
如果
方法1()
和
方法2()
都将通过两个或多个线程中调用,有僵局的好机会,因为如果线程1所获取锁定在执行字符串对象
方法1()
和线程同时执行2所获取锁Integer对象的
方法2 ()
都会在等待对方释放对整数和字符串的锁定以继续进行,这绝不会发生。
该图完全展示了我们的程序,其中一个线程持有一个对象的锁并等待其他线程持有的其他对象锁。
如果你仔细看过上面的代码,那么你可能已经发现死锁的真正原因不是多线程,而是 它们请求锁定的方式 ,如果你提供了一个有序访问,那么问题就会解决,这里是我的固定版本,它可以避免通过避免循环等待而没有先发制人的僵局。
现在不会有任何死锁,因为这两种方法都以相同的顺序访问 Integer 和 String 类的文字锁。因此,如果线程A获得了Integer对象上的锁定,则线程B将不会继续,直到线程A释放整数锁定为止,即使线程B持有字符串锁定,也不会阻塞线程A,因为现在线程B不会期望线程A释放Integer锁定继续进行。
进一步学习
Java基础第1部分
实践中的Java并发
面试问题从“ 什么是死锁? ” 开始,
答案很简单,当两个或更多线程正在等待对方释放锁定并在无限时间卡住时,情况就称为死锁。它只会发生在多任务的情况下。
你如何检测Java中的死锁?
虽然这可能有很多答案,但是我的版本首先会查看代码,如果我看到嵌套同步块或从其他方法调用一个同步方法或尝试锁定不同对象,那么如果开发人员不太在意,就很有可能发生死锁。其他方法是在运行应用程序时实际上被锁定时尝试进行线程转储时发现它,在Linux中可以通过命令 “kill -3” 执行此操作,这将打印应用程序日志文件中所有线程的状态,并且您可以看到哪个线程被锁定在哪个对象上。
其他的方法是使用 jconsole ,它会告诉你究竟哪些线程被锁定以及哪个对象。
编写一个会导致死锁的Java程序?
一旦你回答了这个问题,他们可能会要求你 编写会导致死锁的代码?这是我的一个版本
/ ** * Java程序通过强制循环等待来创建死锁。 * * @作者 WINDOWS 8 * * / public class DeadLockDemo { / * *此方法请求两个锁,第一个字符串然后是整数 * /
public void method1() { synchronized (String.class) { System.out.println("Aquired lock on String.class object"); synchronized (Integer.class) { System.out.println("Aquired lock on Integer.class object"); } } }/ * *此方法也请求相同的两个锁,但完全相同 *相反的顺序,即第一个整数,然后是字符串。 *如果一个线程持有字符串锁,则会造成潜在的死锁 *和其他保持整数锁,他们永远等待对方。 * /
public void method2() { synchronized (Integer.class) { System.out.println("Aquired lock on Integer.class object"); synchronized (String.class) { System.out.println("Aquired lock on String.class object"); } } }}
该图完全展示了我们的程序,其中一个线程持有一个对象的锁并等待其他线程持有的其他对象锁。
如何避免Java中的死锁?
现在面试官进入最后部分,这是我认为最重要的部分之一; 你如何解决僵局? 或者 如何避免Java中的死锁?如果你仔细看过上面的代码,那么你可能已经发现死锁的真正原因不是多线程,而是 它们请求锁定的方式 ,如果你提供了一个有序访问,那么问题就会解决,这里是我的固定版本,它可以避免通过避免循环等待而没有先发制人的僵局。
公共 类 DeadLockFixed { / ** *两种方法现在都以相同顺序请求锁定,首先是Integer,然后是String。 *你也可以做反向例如第一个字符串,然后是整数, *只要这两种方法都请求锁定,两者都可以解决问题 *以一致的顺序。 * /
public void method1() { synchronized (Integer.class) { System.out.println("Aquired lock on Integer.class object"); synchronized (String.class) { System.out.println("Aquired lock on String.class object"); } } } public void method2() { synchronized (Integer.class) { System.out.println("Aquired lock on Integer.class object"); synchronized (String.class) { System.out.println("Aquired lock on String.class object"); } } }}
现在不会有任何死锁,因为这两种方法都以相同的顺序访问 Integer 和 String 类的文字锁。因此,如果线程A获得了Integer对象上的锁定,则线程B将不会继续,直到线程A释放整数锁定为止,即使线程B持有字符串锁定,也不会阻塞线程A,因为现在线程B不会期望线程A释放Integer锁定继续进行。
进一步学习
Java基础第1部分
实践中的Java并发