1. Java死锁的原因
同时阻塞多个线程,其中一个或所有线程都在等待释放资源,并且该资源被其他线程锁定,这导致每个线程都在等待其他线程释放其锁定的资源. 结果,所有线程无法正常结束. 这些是从Internet上其他文档中看到的僵局的四个必要条件:
当以上四个条件都成立时,就会形成死锁. 当然,在死锁的情况下,如果以上任何条件被破坏,则死锁可能会消失. 以下使用Java代码模拟死锁的产生.
模拟两种资源:
public class ThreadResource { public static Object resource1 = new Object(); public static Object resource2 = new Object(); }
模拟线程1占用资源1并申请对资源2的锁定.
public class Thread1 implements Runnable { @Override public void run() { try { System.out.println("Thread1 is running"); synchronized (ThreadResource.resource1) { System.out.println("Thread1 lock resource1"); Thread.sleep(2000);//休眠2s等待线程2锁定资源2 synchronized (ThreadResource.resource2) { System.out.println("Thread1 lock resource2"); } System.out.println("Thread1 release resource2"); } System.out.println("Thread1 release resource1"); } catch (Exception e) { System.out.println(e.getMessage()); } System.out.println("Thread1 is stop"); } }
模拟线程2占用资源2并申请对资源1的锁定:
public class Thread2 implements Runnable { @Override public void run() { try { System.out.println("Thread2 is running"); synchronized (ThreadResource.resource2) { System.out.println("Thread2 lock resource2"); Thread.sleep(2000);//休眠2s等待线程1锁定资源1 synchronized (ThreadResource.resource1) { System.out.println("Thread2 lock resource1"); } System.out.println("Thread2 release resource1"); } System.out.println("Thread2 release resource2"); } catch (Exception e) { System.out.println(e.getMessage()); } System.out.println("Thread2 is stop"); } }
同时运行两个线程:
public class ThreadTest { public static void main(String[] args) { new Thread(new Thread1()).start(); new Thread(new Thread2()).start(); } }
最终输出是:
线程1正在运行
Thread2正在运行
线程1锁定resource1
线程2锁定resource2
该程序从未结束. 这是因为线程1占用了资源1,而线程2已经占用了资源2. 这时,线程1想要使用资源2多线程如何避免死锁,线程2想要使用资源1. 这两个线程都不能屈服,从而导致程序死锁.
2. 避免死锁的Java解决方案
从上面的示例可以看出,当线程正在同步一个对象,然后另一个对象被锁定时,很可能导致死锁. 最好是线程一次只锁定一个对象多线程如何避免死锁,而在锁定对象的过程中不再锁定其他对象,这样就不会导致死锁. 例如,将上述线程更改为以下编写方式可以避免死锁:
public void run() { try { System.out.println("Thread1 is running"); synchronized (ThreadResource.resource1) { System.out.println("Thread1 lock resource1"); Thread.sleep(2000);//休眠2s等待线程2锁定资源2 } System.out.println("Thread1 release resource1"); synchronized (ThreadResource.resource2) { System.out.println("Thread1 lock resource2"); } System.out.println("Thread1 release resource2"); } catch (Exception e) { System.out.println(e.getMessage()); } System.out.println("Thread1 is stop"); }
但是有时业务需要同时锁定两个对象,例如转账业务: A转到B,必须同时锁定A和B帐户. 如果A和B同时进行汇款,则会发生死锁. 此时,可以定义一个规则: 依次锁定帐户的规则. 根据帐户的某个属性(例如id或hasCode),确定锁定的顺序. 也就是说,每个传输事务都将锁定A,然后锁定B(或在锁定A之前锁定B),这不会导致死锁. 例如,根据上述示例,需要同时锁定两个资源,并且可以根据资源的哈希码值确定锁定顺序. 可以这样修改线程:
public class Thread3 implements Runnable { @Override public void run() { try { System.out.println("Thread is running"); if ( ThreadResource.resource1.hashCode() > ThreadResource.resource2.hashCode() ) { //先锁定resource1 synchronized (ThreadResource.resource1) { System.out.println("Thread lock resource1"); Thread.sleep(2000); synchronized (ThreadResource.resource2) { System.out.println("Thread lock resource2"); } System.out.println("Thread release resource2"); } System.out.println("Thread release resource1"); } else { //先锁定resource2 synchronized (ThreadResource.resource2) { System.out.println("Thread lock resource2"); Thread.sleep(2000); synchronized (ThreadResource.resource1) { System.out.println("Thread lock resource1"); } System.out.println("Thread release resource1"); } System.out.println("Thread release resource2"); } } catch (Exception e) { System.out.println(e.getMessage()); } System.out.println("Thread1 is stop"); } }
摘要: 当线程在释放锁定的对象之前需要锁定另一个对象时,死锁很常见,并且此时该对象可能已被另一个线程锁定. 这一次很容易造成死锁. 因此,有必要在开发过程中谨慎使用锁,尤其是要避免在锁中尽可能多地添加锁.
注意: 本文仅代表个人的理解和见解!与我的公司和集团无关!
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-172923-1.html
光看数量就行了
以前最起码吃
颗颗爱心变希望
鄙视