<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                [TOC] ### 死鎖定義 首先我們先來看看死鎖的定義:“死鎖是指兩個或兩個以上的進程在執行過程中,由于競爭資源或者由于彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。”那么我們換一個更加規范的定義:“集合中的每一個進程都在等待只能由本集合中的其他進程才能引發的事件,那么該組進程是死鎖的。” 競爭的資源可以是:鎖、網絡連接、通知事件,磁盤、帶寬,以及一切可以被稱作“資源”的東西。 ## 產生死鎖的四個必要條件: (1) 互斥條件:一個資源每次只能被一個進程使用。 (2) 請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。 (3) 不剝奪條件:進程已獲得的資源,在末使用完之前,不能強行剝奪。 (4) 循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關系。 這四個條件是死鎖的必要條件,只要系統發生死鎖,這些條件必然成立,而只要上述條件之 一不滿足,就不會發生死鎖。 ## 死鎖的解除與預防: 理解了死鎖的原因,尤其是產生死鎖的四個必要條件,就可以最大可能地避免、預防和 解除死鎖。所以,在系統設計、進程調度等方面注意如何不讓這四個必要條件成立,如何確 定資源的合理分配算法,避免進程永久占據系統資源。此外,也要防止進程在處于等待狀態 的情況下占用資源。因此,對資源的分配要給予合理的規劃。 ### 舉個栗子 上面的內容可能有些抽象,因此我們舉個例子來描述,如果此時有一個線程A,按照先鎖a再獲得鎖b的的順序獲得鎖,而在此同時又有另外一個線程B,按照先鎖b再鎖a的順序獲得鎖。如下圖所示: ![死鎖](https://user-gold-cdn.xitu.io/2018/3/19/1623d495a36b4c2c?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 我們用一段代碼來模擬上述過程: ~~~ public static void main(String[] args) { final Object a = new Object(); final Object b = new Object(); Thread threadA = new Thread(new Runnable() { public void run() { synchronized (a) { try { System.out.println("now i in threadA-locka"); Thread.sleep(1000l); synchronized (b) { System.out.println("now i in threadA-lockb"); } } catch (Exception e) { // ignore } } } }); Thread threadB = new Thread(new Runnable() { public void run() { synchronized (b) { try { System.out.println("now i in threadB-lockb"); Thread.sleep(1000l); synchronized (a) { System.out.println("now i in threadB-locka"); } } catch (Exception e) { // ignore } } } }); threadA.start(); threadB.start(); } 復制代碼 ~~~ 程序執行結果如下: ![程序執行結果](https://user-gold-cdn.xitu.io/2018/3/19/1623d495a356a7c7?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 很明顯,程序執行停滯了。 ### 死鎖檢測 在這里,我將介紹兩種死鎖檢測工具 #### 1、Jstack命令 jstack是java虛擬機自帶的一種堆棧跟蹤工具。jstack用于打印出給定的java進程ID或core file或遠程調試服務的Java堆棧信息。 Jstack工具可以用于生成java虛擬機當前時刻的線程快照。**線程快照**是當前java虛擬機內每一條線程**正在執行**的**方法堆棧**的集合,生成線程快照的主要目的是定位線程出現長時間停頓的原因,如`線程間死鎖`、`死循環`、`請求外部資源導致的長時間等待`等。 線程出現停頓的時候通過jstack來查看各個線程的調用堆棧,就可以知道沒有響應的線程到底在后臺做什么事情,或者等待什么資源。 首先,我們通過jps確定當前執行任務的進程號: ~~~ jonny@~$ jps 597 1370 JConsole 1362 AppMain 1421 Jps 1361 Launcher 復制代碼 ~~~ 可以確定任務進程號是1362,然后執行jstack命令查看當前進程堆棧信息: ~~~ jonny@~$ jstack -F 1362 Attaching to process ID 1362, please wait... Debugger attached successfully. Server compiler detected. JVM version is 23.21-b01 Deadlock Detection: Found one Java-level deadlock: ============================= "Thread-1": waiting to lock Monitor@0x00007fea1900f6b8 (Object@0x00000007efa684c8, a java/lang/Object), which is held by "Thread-0" "Thread-0": waiting to lock Monitor@0x00007fea1900ceb0 (Object@0x00000007efa684d8, a java/lang/Object), which is held by "Thread-1" Found a total of 1 deadlock. 復制代碼 ~~~ 可以看到,進程的確存在死鎖,兩個線程分別在等待對方持有的Object對象 #### 2、JConsole工具 Jconsole是JDK自帶的監控工具,在JDK/bin目錄下可以找到。它用于連接正在運行的本地或者遠程的JVM,對運行在Java應用程序的資源消耗和性能進行監控,并畫出大量的圖表,提供強大的可視化界面。而且本身占用的服務器內存很小,甚至可以說幾乎不消耗。 我們在命令行中敲入jconsole命令,會自動彈出以下對話框,選擇進程1362,并點擊“**鏈接**” ![新建連接](https://user-gold-cdn.xitu.io/2018/3/19/1623d495a37b40a7?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 進入所檢測的進程后,選擇“線程”選項卡,并點擊“檢測死鎖” ![檢測死鎖](https://user-gold-cdn.xitu.io/2018/3/19/1623d495a36554b7?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 可以看到以下畫面: ![死鎖檢測結果](https://user-gold-cdn.xitu.io/2018/3/19/1623d495a56c849f?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 可以看到進程中存在死鎖。 以上例子我都是用synchronized關鍵詞實現的死鎖,如果讀者用ReentrantLock制造一次死鎖,再次使用死鎖檢測工具,也同樣能檢測到死鎖,不過顯示的信息將會更加豐富,有興趣的讀者可以自己嘗試一下。 ### 死鎖預防 如果一個線程每次只能獲得一個鎖,那么就不會產生鎖順序的死鎖。雖然不算非常現實,但是也非常正確(一個問題的最好解決辦法就是,這個問題恰好不會出現)。不過關于死鎖的預防,這里有以下幾種方案: #### 1、以確定的順序獲得鎖 如果必須獲取多個鎖,那么在設計的時候需要充分考慮不同線程之前獲得鎖的順序。按照上面的例子,兩個線程獲得鎖的時序圖如下: ![時序圖](https://user-gold-cdn.xitu.io/2018/3/19/1623d495aa379fe7?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 如果此時把獲得鎖的時序改成: ![新時序圖](https://user-gold-cdn.xitu.io/2018/3/19/1623d495c63027dc?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 那么死鎖就永遠不會發生。 針對兩個特定的鎖,開發者可以嘗試按照鎖對象的hashCode值大小的順序,分別獲得兩個鎖,這樣鎖總是會以特定的順序獲得鎖,那么死鎖也不會發生。 ![哲學家進餐](https://user-gold-cdn.xitu.io/2018/3/19/1623d495c647d5c4?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 問題變得更加復雜一些,如果此時有多個線程,都在競爭不同的鎖,簡單按照鎖對象的hashCode進行排序(單純按照hashCode順序排序會出現“環路等待”),可能就無法滿足要求了,這個時候開發者可以使用**銀行家算法**,所有的鎖都按照特定的順序獲取,同樣可以防止死鎖的發生,該算法在這里就不再贅述了,有興趣的可以自行了解一下。 #### 2、超時放棄 當使用synchronized關鍵詞提供的內置鎖時,只要線程沒有獲得鎖,那么就會永遠等待下去,然而Lock接口提供了`boolean tryLock(long time, TimeUnit unit) throws InterruptedException`方法,該方法可以按照固定時長等待鎖,因此線程可以在獲取鎖超時以后,主動釋放之前已經獲得的所有的鎖。通過這種方式,也可以很有效地避免死鎖。 還是按照之前的例子,時序圖如下: ![時序圖](https://user-gold-cdn.xitu.io/2018/3/19/1623d495c8474263?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) ### 其他形式的死鎖 我們再來回顧一下死鎖的定義,“死鎖是指兩個或兩個以上的進程在執行過程中,由于競爭資源或者由于彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。” 死鎖條件里面的競爭資源,可以是線程池里的線程、網絡連接池的連接,數據庫中數據引擎提供的鎖,等等一切可以被稱作競爭資源的東西。 #### 1、線程池死鎖 用個例子來看看這個死鎖的特征: ~~~ final ExecutorService executorService = Executors.newSingleThreadExecutor(); Future<Long> f1 = executorService.submit(new Callable<Long>() { public Long call() throws Exception { System.out.println("start f1"); Thread.sleep(1000);//延時 Future<Long> f2 = executorService.submit(new Callable<Long>() { public Long call() throws Exception { System.out.println("start f2"); return -1L; } }); System.out.println("result" + f2.get()); System.out.println("end f1"); return -1L; } }); 復制代碼 ~~~ 在這個例子中,線程池的任務1依賴任務2的執行結果,但是線程池是單線程的,也就是說任務1不執行完,任務2永遠得不到執行,那么因此造成了死鎖。原因圖解如下: ![線程池死鎖](https://user-gold-cdn.xitu.io/2018/3/19/1623d495d05cfc0b?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 執行jstack命令,可以看到如下內容: ~~~ "pool-1-thread-1" prio=5 tid=0x00007ff4c10bf800 nid=0x3b03 waiting on condition [0x000000011628c000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x00000007ea51cf40> (a java.util.concurrent.FutureTask$Sync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186) at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:834) at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:994) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1303) at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:248) at java.util.concurrent.FutureTask.get(FutureTask.java:111) at com.test.TestMain$1.call(TestMain.java:49) at com.test.TestMain$1.call(TestMain.java:37) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334) at java.util.concurrent.FutureTask.run(FutureTask.java:166) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:722) 復制代碼 ~~~ 可以看到當前線程wait在java.util.concurrent.FutureTask對象上。 解決辦法:擴大線程池線程數 or 任務結果之間不再互相依賴。 #### 2、網絡連接池死鎖 同樣的,在網絡連接池也會發生死鎖,假設此時有兩個線程A和B,兩個數據庫連接池N1和N2,連接池大小都只有1,如果線程A按照先N1后N2的順序獲得網絡連接,而線程B按照先N2后N1的順序獲得網絡連接,并且兩個線程在完成執行之前都不釋放自己已經持有的鏈接,因此也造成了死鎖。 ~~~ // 連接1 final MultiThreadedHttpConnectionManager connectionManager1 = new MultiThreadedHttpConnectionManager(); final HttpClient httpClient1 = new HttpClient(connectionManager1); httpClient1.getHttpConnectionManager().getParams().setMaxTotalConnections(1); //設置整個連接池最大連接數 // 連接2 final MultiThreadedHttpConnectionManager connectionManager2 = new MultiThreadedHttpConnectionManager(); final HttpClient httpClient2 = new HttpClient(connectionManager2); httpClient2.getHttpConnectionManager().getParams().setMaxTotalConnections(1); //設置整個連接池最大連接數 ExecutorService executorService = Executors.newFixedThreadPool(2); executorService.submit(new Runnable() { public void run() { try { PostMethod httpost = new PostMethod("http://www.baidu.com"); System.out.println(">>>> Thread A execute 1 >>>>"); httpClient1.executeMethod(httpost); Thread.sleep(5000l); System.out.println(">>>> Thread A execute 2 >>>>"); httpClient2.executeMethod(httpost); System.out.println(">>>> End Thread A>>>>"); } catch (Exception e) { // ignore } } }); executorService.submit(new Runnable() { public void run() { try { PostMethod httpost = new PostMethod("http://www.baidu.com"); System.out.println(">>>> Thread B execute 2 >>>>"); httpClient2.executeMethod(httpost); Thread.sleep(5000l); System.out.println(">>>> Thread B execute 1 >>>>"); httpClient1.executeMethod(httpost); System.out.println(">>>> End Thread B>>>>"); } catch (Exception e) { // ignore } } }); 復制代碼 ~~~ 整個過程圖解如下: ![連接池死鎖](https://user-gold-cdn.xitu.io/2018/3/19/1623d495d24acafa?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 在死鎖產生后,我們用jstack工具查看一下當前線程堆棧信息,可以看到如下內容: ~~~ "pool-1-thread-2" prio=5 tid=0x00007faa7909e800 nid=0x3b03 in Object.wait() [0x0000000111e5d000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x00000007ea73f498> (a org.apache.commons.httpclient.MultiThreadedHttpConnectionManager$ConnectionPool) at org.apache.commons.httpclient.MultiThreadedHttpConnectionManager.doGetConnection(MultiThreadedHttpConnectionManager.java:518) - locked <0x00000007ea73f498> (a org.apache.commons.httpclient.MultiThreadedHttpConnectionManager$ConnectionPool) at org.apache.commons.httpclient.MultiThreadedHttpConnectionManager.getConnectionWithTimeout(MultiThreadedHttpConnectionManager.java:416) at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:153) at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397) at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323) at com.test.TestMain$2.run(TestMain.java:79) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334) at java.util.concurrent.FutureTask.run(FutureTask.java:166) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:722) "pool-1-thread-1" prio=5 tid=0x00007faa7a039800 nid=0x3a03 in Object.wait() [0x0000000111d5a000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x00000007ea73e0d0> (a org.apache.commons.httpclient.MultiThreadedHttpConnectionManager$ConnectionPool) at org.apache.commons.httpclient.MultiThreadedHttpConnectionManager.doGetConnection(MultiThreadedHttpConnectionManager.java:518) - locked <0x00000007ea73e0d0> (a org.apache.commons.httpclient.MultiThreadedHttpConnectionManager$ConnectionPool) at org.apache.commons.httpclient.MultiThreadedHttpConnectionManager.getConnectionWithTimeout(MultiThreadedHttpConnectionManager.java:416) at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:153) at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397) at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323) at com.test.TestMain$1.run(TestMain.java:61) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334) at java.util.concurrent.FutureTask.run(FutureTask.java:166) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:722) 復制代碼 ~~~ 當然,我們在這里只是一些極端情況的假定,假如線程在使用完連接池之后很快就歸還,在歸還連接數后才占用下一個連接池,那么死鎖也就不會發生。 參考鏈接:《死鎖終極篇》https://juejin.im/post/5aaf6ee76fb9a028d3753534
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看