是指兩個或兩個以上的進程在執行過程當中,因爭奪資源而形成的一種互相等待的現象,若無外力做用,它們都將沒法推動下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。 因爲資源佔用是互斥的,當某個進程提出申請資源後,使得有關進程在無外力協助下,永遠分配不到必需的資源而沒法繼續運行,這就產生了一種特殊現象:死鎖。」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線程,每間隔一段時間檢查一下是否發生死鎖,若是有則預警、記錄日誌、或中斷該線程避免死鎖)