是指两个或两个以上的进程在执行过程当中,因争夺资源而形成的一种互相等待的现象,若无外力做用,它们都将没法推动下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。 因为资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力协助下,永远分配不到必需的资源而没法继续运行,这就产生了一种特殊现象:死锁。”java
指事物1可使用资源,但它让其余事物先使用资源;事物2可使用资源,但它也让其余事物先使用资源,因而二者一直谦让,都没法使用资源。数组
避免活锁的简单方法是采用先来先服务的策略。当多个事务请求封锁同一数据对象时,封锁子系统按请求封锁的前后次序对事务排队,数据对象上的锁一旦释放就批准申请队列中第一个事务得到锁。多线程
使用tryLock()方法来防止多线程死锁。dom
tryLock()方法:尝试获取一把锁,若是获取成功返回true,若是还拿不到锁,就返回false。ide
public class DeadLock1 { private static Lock lock1 = new ReentrantLock(); private static Lock lock2 = new ReentrantLock(); public static void deathLock() { new Thread() { @Override public void run() { while (true) { if (lock1.tryLock()) { try { //若是获取成功则执行业务逻辑,若是获取失败,则释放lock1的锁,自旋从新尝试得到锁 if (lock2.tryLock()) { try { System.out.println("Thread1:已成功获取 lock1 and lock2 ..."); break; } finally { lock2.unlock(); } } } finally { lock1.unlock(); } } System.out.println("Thread1:获取锁失败,从新获取---"); try { //防止发生活锁 TimeUnit.NANOSECONDS.sleep(new Random().nextInt(100)); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread() { @Override public void run() { while (true) { if (lock2.tryLock()) { try { //若是获取成功则执行业务逻辑,若是获取失败,则释放lock2的锁,自旋从新尝试得到锁 if (lock1.tryLock()) { try { System.out.println("Thread2:已成功获取 lock2 and lock1 ..."); break; } finally { lock1.unlock(); } } } finally { lock2.unlock(); } } System.out.println("Thread2:获取锁失败,从新获取---"); try { //防止发生活锁 TimeUnit.NANOSECONDS.sleep(new Random().nextInt(100)); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 5; i++) { deathLock(); } } }
该示例启动两个线程。线程1首先获取lock1的锁,而后再获取lock2的锁;线程2首先获取lock2的锁,而后再获取lock1的锁。这样若是这时线程1得到了lock1的锁,同时线程2得到lock2的锁,而后线程1尝试去得到lock2的锁,线程2尝试得到线程1的锁,就会形成死锁。spa
咱们这里使用tryLock来获取两个锁,若是一个线程不能同时获取两把锁,那么就回退并自旋从新尝试(使用while循环)。再使用TimeUnit.NANOSECONDS.sleep(new Random().nextInt(100));随机休眠一段时间,从而下降发生活锁的可能性。若是处理成功,则使用break跳出循环。线程
使用tryLock(long timeout, TimeUnit unit) 方法来防止多线程死锁。日志
tryLock(long time, TimeUnit unit)方法和tryLock()方法是相似的,只不过区别在于这个方法在拿不到锁时会等待必定的时间,在时间期限以内若是还拿不到锁,就返回false。若是一开始拿到锁或者在等待期间内拿到了锁,则返回true。code
import java.util.Random; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class DeadLock2 { private static ReentrantLock lock1 = new ReentrantLock(); private static ReentrantLock lock2 = new ReentrantLock(); public static void deathLock() { new Thread() { @Override public void run() { while (true) { try { if (lock1.tryLock(10, TimeUnit.MILLISECONDS)) { try { //若是获取成功则执行业务逻辑,若是获取失败,则释放lock1的锁,自旋从新尝试得到锁 if (lock2.tryLock(10, TimeUnit.MILLISECONDS)) { System.out.println("Thread1:已成功获取 lock1 and lock2 ..."); break; } } catch (InterruptedException e) { e.printStackTrace(); } finally { if(lock2.isHeldByCurrentThread()){ lock2.unlock(); } } } } catch (InterruptedException e) { e.printStackTrace(); } finally { if(lock1.isHeldByCurrentThread()){ lock1.unlock(); } } System.out.println("Thread1:获取锁失败,从新获取---"); try { TimeUnit.NANOSECONDS.sleep(new Random().nextInt(100)); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread() { @Override public void run() { while (true) { try { if (lock2.tryLock(10, TimeUnit.MILLISECONDS)) { try { //若是获取成功则执行业务逻辑,若是获取失败,则释放lock1的锁,自旋从新尝试得到锁 if (lock1.tryLock(10, TimeUnit.MILLISECONDS)) { System.out.println("Thread2:已成功获取 lock2 and lock1 ..."); break; } } catch (InterruptedException e) { e.printStackTrace(); } finally { if(lock1.isHeldByCurrentThread()){ lock1.unlock(); } } } } catch (InterruptedException e) { e.printStackTrace(); } finally { if(lock2.isHeldByCurrentThread()){ lock2.unlock(); } } System.out.println("Thread2:获取锁失败,从新获取---"); try { TimeUnit.NANOSECONDS.sleep(new Random().nextInt(100)); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 5; i++) { deathLock(); } } }
该示例同示例一。咱们这里使用tryLock(long time, TimeUnit unit)来获取两个锁,若是一个线程不能同时获取两把锁,那么就回退并自旋从新尝试(使用while循环)。在使用TimeUnit.NANOSECONDS.sleep(new Random().nextInt(100));随机休眠一段时间,从而下降发生活锁的可能性。若是处理成功,则使用break跳出循环。对象
使用lockInterruptibly()得到锁,若是发生死锁,调用线程interrupt来消除死锁。
ReentrantLock.lockInterruptibly容许在等待时由其它线程调用等待线程的Thread.interrupt方法来中断等待线程的等待而直接返回,这时不用获取锁,而会抛出一个InterruptedException。而ReentrantLock.lock方法不容许Thread.interrupt中断,即便检测到Thread.isInterrupted,同样会继续尝试获取锁,失败则继续休眠。只是在最后获取锁成功后再把当前线程置为interrupted状态。
public class DeadLock3 { private static Lock lock1 = new ReentrantLock(); private static Lock lock2 = new ReentrantLock(); public static void deathLock() { new Thread() { @Override public void run() { try { lock1.lockInterruptibly(); try { TimeUnit.SECONDS.sleep(1); lock2.lockInterruptibly(); System.out.println("thread 1 ..."); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock2.unlock(); } } catch (InterruptedException e1) { e1.printStackTrace(); } finally { lock1.unlock(); } } }.start(); new Thread() { @Override public void run() { try { lock2.lockInterruptibly(); try { TimeUnit.SECONDS.sleep(1); lock1.lockInterruptibly(); System.out.println("thread 1 ..."); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock1.unlock(); } } catch (InterruptedException e1) { e1.printStackTrace(); } finally { lock2.unlock(); } } }.start(); } public static void main(String[] args) throws InterruptedException { deathLock(); TimeUnit.SECONDS.sleep(2); checkDeadLock(); } //基于JMX获取线程信息 public static void checkDeadLock() { //获取Thread的MBean ThreadMXBean mbean = ManagementFactory.getThreadMXBean(); //查找发生死锁的线程,返回线程id的数组 long[] deadLockThreadIds = mbean.findDeadlockedThreads(); System.out.println("---" + deadLockThreadIds); if (deadLockThreadIds != null) { //获取发生死锁的线程信息 ThreadInfo[] threadInfos = mbean.getThreadInfo(deadLockThreadIds); //获取JVM中全部的线程信息 Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces(); for (Entry<Thread, StackTraceElement[]> entry : map.entrySet()) { for (int i = 0; i < threadInfos.length; i++) { Thread t = entry.getKey(); if (t.getId() == threadInfos[i].getThreadId()) { //中断发生死锁的线程 t.interrupt(); //打印堆栈信息 // for (StackTraceElement ste : entry.getValue()) { // // System.err.println("t" + ste.toString().trim()); // } } } } } } }
咱们这里使用lockInterruptibly()方法来获取锁,咱们这里使用线程1获取lock1 休眠1秒,线程2获取lock2 休眠1秒,1秒事后,而后线程1再获取lock2,线程2再去得到lock1就会发生死锁。这是咱们又执行了checkDeadLock()方法,来检查JVM中是否有死锁,若是有死锁,则把发生死锁的线程执行interrupt()方法,使该线程响应中断,从而避免发生死锁。(实际应用中,检查死锁能够单独开启一个daemon线程,每间隔一段时间检查一下是否发生死锁,若是有则预警、记录日志、或中断该线程避免死锁)