死鎖是指在多線程環境下的這麼一種場景,兩個(多個)線程在分別拿到本身的鎖時嘗試獲取對方的鎖,因爲必須等待對方釋放鎖才能獲取,然而雙方誰也不願先釋放本身的鎖, 致使雙方誰都沒法繼續執行。java
經過一個實現runnable接口的類實例做爲兩個線程的執行對象,在該類中有兩個Object的靜態變量做爲鎖.經過該類的一個開關變量實如今同一個run方法中執行兩段不一樣的邏輯,一個先獲取鎖1, 再獲取鎖2,另外一個分支則恰好相反。多線程
爲了使第一個執行的線程在拿到第二個鎖以前失去cpu執行權,方便構造死鎖場景,在嘗試獲取第二個鎖以前,讓線程休眠一段時間,由於sleep()方法不會釋放鎖。ide
實現死鎖的方法有兩種,一種是使用synchronized同步代碼塊,另外一種是使用reentrantlock重入鎖。測試
public class TestDeadLock implements Runnable { //開關 private boolean flag; //鎖1 private static Object lock1 = new Object(); //鎖2 private static Object lock2 = new Object(); public TestDeadLock(boolean flag) { this.flag = flag; } @Override public void run() { if (flag) { synchronized (lock1) { System.out.println(flag + "線程拿到了lock1"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock2) { System.out.println(flag + "線程拿到了lock2"); } } } else { synchronized (lock2) { System.out.println(flag + "線程拿到了lock2"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock1) { System.out.println(flag + "線程拿到了lock1"); } } } } public static void main(String[] args) { Thread thread1 = new Thread(new TestDeadLock(true)); Thread thread2 = new Thread(new TestDeadLock(false)); thread1.start(); thread2.start(); } }
true線程拿到了lock1 false線程拿到了lock2
public class TestDeadLock2 implements Runnable{ private boolean flag; private static ReentrantLock lock1=new ReentrantLock(); private static ReentrantLock lock2=new ReentrantLock(); public TestDeadLock2(boolean flag) { this.flag = flag; } @Override public void run() { try { if(flag){ lock1.lock(); System.out.println(flag + "線程獲取了Lock1"); TimeUnit.SECONDS.sleep(1); lock2.lock(); System.out.println(flag+"線程獲取了Lock2"); }else{ lock2.lock(); System.out.println(flag + "線程獲取了Lock2"); TimeUnit.SECONDS.sleep(1); lock1.lock(); System.out.println(flag+"線程獲取了Lock1"); } } catch (InterruptedException e) { e.printStackTrace(); } finally { if(lock1.isHeldByCurrentThread()){ lock1.unlock(); } if(lock2.isHeldByCurrentThread()){ lock2.unlock(); } } } public static void main(String[] args) throws InterruptedException { Thread thread1=new Thread(new TestDeadLock2(true)); Thread thread2=new Thread(new TestDeadLock2(false)); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println("主線程已結束"); } }
false線程獲取了Lock2 true線程獲取了Lock1
Java中的ReentrantLock和synchronized兩種鎖定機制的對比ui
總的來講,ReentrantLock所提供的功能比Synchronized要豐富的多,好比this
public void lockInterruptibly() throws InterruptedException
spa
public class TestDeadLock3 implements Runnable { private boolean flag; static ReentrantLock lock1 = new ReentrantLock(); static ReentrantLock lock2 = new ReentrantLock(); public TestDeadLock3(boolean flag) { this.flag = flag; } @Override public void run() { try { if (flag) { //可中斷地加鎖 lock1.lockInterruptibly(); System.out.println(flag + "線程獲取了lock1"); TimeUnit.SECONDS.sleep(1); lock2.lockInterruptibly(); System.out.println(flag + "線程獲取了lock2"); } else { lock2.lockInterruptibly(); System.out.println(flag + "線程獲取lock2"); TimeUnit.SECONDS.sleep(1); lock1.lockInterruptibly(); System.out.println(flag + "線程獲取了lock1"); } } catch (InterruptedException e) { e.printStackTrace(); } finally { if (lock1.isHeldByCurrentThread()) { lock1.unlock(); System.out.println(flag + "線程釋放lock1鎖"); } if (lock2.isHeldByCurrentThread()) { lock2.unlock(); System.out.println(flag + "線程釋放lock2鎖"); } System.out.println(flag + "線程已退出"); } } public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(new TestDeadLock3(true)); Thread thread2 = new Thread(new TestDeadLock3(false)); thread1.start(); thread2.start(); //主線程休眠5秒 TimeUnit.SECONDS.sleep(5); thread1.interrupt(); } }
true線程獲取了lock1 false線程獲取lock2 true線程釋放lock1鎖 java.lang.InterruptedException false線程獲取了lock1 at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:896) true線程已退出 at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1221) false線程釋放lock1鎖 at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340) false線程釋放lock2鎖 at com.akane.test.reentrantlock.TestDeadLock3.run(TestDeadLock3.java:31) false線程已退出 at java.lang.Thread.run(Thread.java:744) Process finished with exit code 0
synchronized在獲鎖的過程當中是不能被中斷的,意思是說若是產生了死鎖,則不可能被中斷(請參考後面的測試例子)。與synchronized功能類似的reentrantLock.lock()方法也是同樣,它也不可中斷的,即若是發生死鎖,那麼reentrantLock.lock()方法沒法終止,若是調用時被阻塞,則它一直阻塞到它獲取到鎖爲止。可是若是調用帶超時的tryLock方法reentrantLock.tryLock(long timeout, TimeUnit unit),那麼若是線程在等待時被中斷,將拋出一個InterruptedException異常,這是一個很是有用的特性,由於它容許程序打破死鎖。你也能夠調用reentrantLock.lockInterruptibly()方法,它就至關於一個超時設爲無限的tryLock方法.net
主線程對Thread1進行了中斷,thread1拋出異常,異常被捕獲,在finally中釋放thread1得到的鎖,線程2得到須要的鎖,該線程得以繼續執行,死鎖就被解決了線程
固然,ReentrantLock還提供了另一個更好的方法解決死鎖問題,那就是使用tryLock()方法,該方法會嘗試得到鎖,若是成功,返回true,失敗則返回false。該方法不等待或等待一段時間就返回。code
public boolean tryLock()
當即返回public boolean tryLock(long timeout, TimeUnit unit)
等待一段時間後返回
死鎖的緣由在於吃着碗裏的看着鍋裏的,咱們讓線程拿到一個鎖以後不管是否拿到第二個鎖,都釋放已經拿到的鎖,能夠將此邏輯放入finally中,配合外層的while(true)屢次重複嘗試,若是成功獲取兩個鎖,則釋放兩個鎖的同時推出while循環,如下是代碼實現,線程睡眠時間由1秒改成1毫秒,減小測試須要的時間
public class TestDeadLock4 implements Runnable{ private boolean flag; static ReentrantLock lock1 = new ReentrantLock(); static ReentrantLock lock2 = new ReentrantLock(); //統計發生死鎖的次數 private static int count; public TestDeadLock4(boolean flag) { this.flag = flag; } @Override public void run() { if(flag){ while (true) { if(lock1.tryLock()){ System.out.println(flag+"線程得到了lock1"); try { TimeUnit.MILLISECONDS.sleep(1); try { if(lock2.tryLock()){ System.out.println(flag+"得到了lock2"); } } finally { //同時得到Lock1和lock2,沒有發生死鎖,任務完成,退出循環 if(lock1.isHeldByCurrentThread()&&lock2.isHeldByCurrentThread()){ System.out.println(flag+"線程執行完畢"+"---------------------"); lock1.unlock(); lock2.unlock(); break; }else{ //說明發生了死鎖,只須要釋放lock1 //統計變量也要注意多線程問題 synchronized (TestDeadLock4.class) { count++; System.out.println("發生了"+count+"次死鎖"); } lock1.unlock(); } } } catch (InterruptedException e) { e.printStackTrace(); } } } }else{ while (true) { if(lock2.tryLock()){ System.out.println(flag+"線程得到了lock2"); try { TimeUnit.MILLISECONDS.sleep(1); try { if(lock1.tryLock()){ System.out.println(flag+"線程得到了lock1"); } } finally { if(lock1.isHeldByCurrentThread()&&lock2.isHeldByCurrentThread()){ System.out.println(flag+"線程執行完畢"+"---------------------"); lock1.unlock(); lock2.unlock(); break; }else{ synchronized (TestDeadLock4.class) { count++; System.out.println("發生了"+count+"次死鎖"); } lock2.unlock(); } } } catch (InterruptedException e) { e.printStackTrace(); } } } } } public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(new TestDeadLock4(true)); Thread thread2 = new Thread(new TestDeadLock4(false)); thread1.start(); thread2.start(); } }
發生了3335次死鎖 false線程得到了lock2 發生了3336次死鎖 true線程得到了lock1 發生了3337次死鎖 false線程得到了lock2 發生了3338次死鎖 true線程得到了lock1 發生了3339次死鎖 false線程得到了lock2 發生了3340次死鎖 true線程得到了lock1 發生了3341次死鎖 true得到了lock2 true線程執行完畢--------------------- false線程得到了lock2 false線程得到了lock1 false線程執行完畢--------------------- Process finished with exit code 0