死锁
一个经典的多线程问题。
当一个线程永远地持有一个锁,并且其他线程都尝试去获得这个锁时,那么它们将永远被阻塞,这个我们都知道。如果线程A持有锁L并且想获得锁M,线程B持有锁M并且想获得锁L,那么这两个线程将永远等待下去,这种情况就是最简单的死锁形式。
在数据库系统的设计中考虑了监测死锁以及从死锁中恢复,数据库如果监测到了一组事务发生了死锁时,将选择一个牺牲者并放弃这个事务。Java虚拟机解决死锁问题方面并没有数据库这么强大,当一组Java线程发生死锁时,这两个线程就永远不能再使用了,并且由于两个线程分别持有了两个锁,那么这两段同步代码/代码块也无法再运行了----除非终止并重启应用。
死锁是设计的BUG,问题比较隐晦。不过死锁造成的影响很少会立即显现出来,一个类可能发生死锁,并不意味着每次都会发生死锁,这只是表示有可能。当死锁出现时,往往是在最糟糕的情况----高负载的情况下。
实现死锁的方式有如下几种:
方式一(在synchronized的方法中调用对方对象的方法)
package deadlock;/** * Created by lkmc2 on 2018/1/27. */class MyThread1 implements Runnable { private String objName; private MyThread1 lockObj; //用来持有对方的对象 public MyThread1(String objName) { this.objName = objName; } public void setLockObj(MyThread1 lockObj) { this.lockObj = lockObj; } public synchronized void intoB() { String currentThread = Thread.currentThread()。getName(); System.out.printf(“线程%s进入intoB,objName=%s ”, currentThread, objName); try { Thread.sleep(2000); } catch (InterruptedException x) { } System.out.printf(“线程%s尝试进入intoA,objName=%s ”, currentThread, objName); lockObj.intoA(); System.out.printf(“线程%s离开intoB(),objName=%s ”, currentThread, objName); } public synchronized void intoA() { String currentThread = Thread.currentThread()。getName(); System.out.printf(“线程%s进入intoA,objName=%s ”, currentThread, objName); try { Thread.sleep(500); } catch (InterruptedException x) { } System.out.printf(“线程%s尝试进入intoB,objName=%s ”, currentThread, objName); lockObj.intoB(); System.out.printf(“线程%s离开intoA(),objName=%s ”, currentThread, objName); } @Override public void run() { if (objName.equals(“A”)) { intoA(); } else { intoB(); } }}public class Test1 { public static void main(String[] args) throws InterruptedException { final MyThread1 obj1 = new MyThread1(“A”); final MyThread1 obj2 = new MyThread1(“B”); obj1.setLockObj(obj2); //持有对方的对象 obj2.setLockObj(obj1); Thread threadA = new Thread(obj1, “threadA”); threadA.start(); Thread.sleep(200); Thread threadB = new Thread(obj2, “threadB”); threadB.start(); }}
方式二(在synchronized代码块中锁定不同的对象)
package deadlock;/** * Created by lkmc2 on 2018/1/27. */class MyThread2 implements Runnable { private final String[] lock = {“A”, “B”}; private void intoA(String threadName, int index) { System.out.println(“线程” + threadName + “尝试进入intoA”); synchronized (lock[index]) { System.out.println(“线程” + threadName + “进入intoA”); sleep(200); intoB(threadName, 1); } } private void intoB(String threadName, int index) { System.out.println(“线程” + threadName + “尝试进入intoB”); synchronized (lock[index]) { System.out.println(“线程” + threadName + “进入intoB”); sleep(200); intoA(threadName, 0); } } @Override public void run() { String threadName = Thread.currentThread()。getName(); if (“threadA”.equals(threadName)) { intoA(threadName, 0); } else { intoB(threadName, 1); } } public void sleep(int time) { try { Thread.sleep(time); } catch (InterruptedException e) { e.printStackTrace(); } }}public class Test2 { public static void main(String[] args) throws InterruptedException { MyThread2 thread1 = new MyThread2(); new Thread(thread1, “threadA”)。start(); Thread.sleep(5); new Thread(thread1, “threadB”)。start(); }}
方式三(嵌套两层synchronized锁并相互锁定所需的对象)
package deadlock;/** * Created by lkmc2 on 2018/1/27. */class LockA implements Runnable { @Override public void run() { System.out.println(“LockA开始执行……”); while (true) { synchronized (Test3.obj1) { System.out.println(“LockA锁住obj1……”); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (Test3.obj2) { System.out.println(“LockA锁住obj2……”); } System.out.println(“LockA释放obj2……”); } System.out.println(“LockA释放obj1……”); } }}class LockB implements Runnable { @Override public void run() { System.out.println(“LockB开始执行……”); while (true) { synchronized (Test3.obj2) { System.out.println(“LockB锁住obj1……”); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (Test3.obj1) { System.out.println(“LockB锁住obj2……”); } System.out.println(“LockB释放obj2……”); } System.out.println(“LockB释放obj1……”); } }}public class Test3 { public static final byte[] obj1 = new byte[0]; public static final byte[] obj2 = new byte[0]; public static void main(String[] args) { LockA lockA = new LockA(); LockB lockB = new LockB(); new Thread(lockA)。start(); new Thread(lockB)。start(); }}
避免死锁的方式
既然可能产生死锁,那么接下来,讲一下如何避免死锁。
1、让程序每次至多只能获得一个锁。当然,在多线程环境下,这种情况通常并不现实
2、设计时考虑清楚锁的顺序,尽量减少嵌在的加锁交互数量
3、既然死锁的产生是两个线程无限等待对方持有的锁,那么只要等待时间有个上限不就好了。当然synchronized不具备这个功能,但是我们可以使用Lock类中的tryLock方法去尝试获取锁,这个方法可以指定一个超时时限,在等待超过该时限之后变回返回一个失败信息
更多武汉IT培训相关资讯,请扫描下方二维码