<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國際加速解決方案。 廣告
                ## 14 僵持不下—死鎖詳解 > 天才就是這樣,終身努力,便成天才。 > ——門捷列夫 前面幾節講解了并發的三大特性 - 原子性、可見性、有序性。解決這些問題的關鍵就是同步,而一種重要的同步方式就是加鎖,所謂的加鎖就是某個線程聲明某個資源暫時由我獨享,等其用完后,此線程解鎖,也就是放棄對該資源的占有。如果有其它線程等待使用該資源,那么會再次對此資源加鎖。所謂的死鎖,其實就是因為某種原因,達不到解鎖的條件,導致某線程對資源的占有無法釋放,其他線程會一直等待其解鎖,而被一直 block 住。 ## 1\. 死鎖產生原因 產生死鎖的原因很多,我們逐個來看一下: 1. 交叉死鎖 A 線程持有資源 R1 的鎖時,想要獲取 R2 的鎖。而線程 B 此時持有 R2 的鎖,想要獲取 R1 的鎖。結果就是兩個線程互相等待對方釋放,并且一直等待下去。這就像兩個小孩打架摟抱在一起,A 說:你放手!B 說:你先放手!A 說:你先放手我就放手!B 說:憑什么我先放手,你先放手我就放手!瞧,是不是死鎖了,最后估計還得繼續打下去。 ![圖片描述](https://img.mukewang.com/5da52abd000113e409250468.jpg) 2. 內存不足 某系統內存 20M,兩個線程正在分別執行任務,各自已經使用了 10M 內存。但是執行到一半時需要更大的內存,但是系統已經沒有內存可供使用。那么兩個線程都會等待對方執行完畢 時釋放內存。這就造成了兩個線程互相等待,從而形成死鎖。 3. 一問一答式的數據交換 所謂的一問一答式數據交換就是客戶端發送請求,服務端返回響應。如果在交互過程中出現了數據的丟失,雙方產生誤解,以為對方沒有收到消息,陷入等待之中。如果此時沒有設置 timeout,就會造成互相的等待一直持續下去,從而形成死鎖。 4. 數據庫鎖 如果某個線程對數據庫表或者行加鎖,但是意外導致沒能正確釋放鎖,而其他線程則會等待數據庫鎖的釋放,從而陷入死鎖。 5. 文件鎖 某個線程獲取文件鎖后開始執行。但是執行過程中意外退出,而沒能釋放鎖。那么其他等待該文件鎖的線程將會一直等待,直到系統釋放文件句柄的資源。 6. 死循環 假如某個線程,由于編碼問題,在對資源加鎖后,陷入死循環,導致一致無法釋放鎖。 ## 2 . 死鎖舉例 下面我們看一個交叉死鎖的例子,來切身感受下死鎖是如何煉成的。例子很簡單,DeadLock 類有一個讀方法和一個寫方法,讀方法獲取讀鎖后,又嘗試獲取寫鎖。而寫方法獲取寫鎖后,又嘗試獲取讀鎖。這種情況下,兩個線程會互相等待對方的鎖釋放,從而形成了死鎖。我們看下面的代碼: ~~~java public class DeadLock { private final String write_lock = new String(); private final String read_lock = new String(); public void read() { synchronized (read_lock) { System.out.println(Thread.currentThread().getName() + " got read lock and then i want to write"); synchronized (write_lock) { System.out.println(Thread.currentThread().getName() + " got read lock and write lock"); } } } public void write() { synchronized (write_lock) { System.out.println(Thread.currentThread().getName() + " got write lock and then i want to read"); synchronized (read_lock) { System.out.println(Thread.currentThread().getName() + " got write lock and read lock"); } } } public static void main(String[] args) { DeadLock deadLock = new DeadLock(); new Thread(() -> { while (true) { deadLock.read(); } },"read-first-thread").start(); new Thread(() -> { while (true) { deadLock.write(); } },"write-first-thread").start(); } } ~~~ 注意 mian 方法中使用了 lambda 表達式為 thread 提供了 run 方法的現實。免去了我們編寫兩個實現 run 方法類的麻煩。這段程序運行后,控制臺輸出如下: ~~~ read-first-thread got read lock and then i want to write read-first-thread got read lock and write lock read-first-thread got read lock and then i want to write read-first-thread got read lock and write lock read-first-thread got read lock and then i want to write read-first-thread got read lock and write lock read-first-thread got read lock and then i want to write write-first-thread got write lock and then i want to read ~~~ 可以看到在 write 線程啟動前,一切正常。read-first-thread 線程能夠先后獲得 read 鎖和 write 鎖。但是當 write 線程啟動后,立刻出現了問題,日志不再打印,而是停留在 write 線程等待 read 鎖這一步。這是因為已經死鎖了。 read 線程在等 write 線程釋放寫鎖,而 write 線程在等 read 線程釋放讀鎖。兩個線程就會如此一直等下去了。 ## 3\. 死鎖診斷 線程死鎖可以通過 java 的監控工具來查看。此類工具很多,例如 jstack、jconsole、jprofile 等。下面我們看一下 Java 內置的 jconsole。如果你安裝了 JDK,設置好了環境變量,那么可以直接在控制臺輸入 jconsole 來運行,界面如下: ![圖片描述](https://img.mukewang.com/5da52c2f0001ce8a04110471.jpg) 在本地進程中我們可以看到剛才運行的 DeadLock。選中后點擊連接。在下一個界面上面的菜單選擇線程。在下方左側框中可以看到 write 和 read 兩個線程。然后我們點擊檢查死鎖,顯示如下圖: ![圖片描述](https://img.mukewang.com/5da52c470001ee7808490679.jpg) 點擊某個線程,如 write-first-thread,右側框中出現此線程的狀態,可以看到狀態為 java.lang.String@1adb219c 上的 BLOCKED,擁有者: read-first-thread。意思是此線程在 java.lang.String@1adb219c 上被 block 住了,這個 String 對象其實就是 read\_lock 對象,目前鎖的擁有者是 read-first-thread。我們再查看另外一個線程 read-first-thread,可以看到正好是反過來的。這兩個線程互相 block 住了。 ![圖片描述](https://img.mukewang.com/5da52c500001eb3008560331.jpg) ## 4\. 總結 本節中我們列舉了多種引起死鎖的原因,這對我們分析死鎖的產生很有幫助,也有助于我們從代碼層面找到可能導致死鎖的風險。后面通過舉例,更為形象的切身感受到死鎖的產生過程。即使我們知道死鎖產生的原理,但也還是很可能寫出導致死鎖的代碼,那么出現死鎖或者疑似死鎖的時候應該怎么辦呢?最后也給出了答案。 至此本章已經結束。本章主要講解了多線程程序中會遇到的問題。并且針對如何解決這些問題,做了一些簡單的講解。在接下來的一章中,我們將會深入學習 Java 為我們提供的解決這些問題的工具。我們不但在遇到問題的時候要知道如何解決,還應該了解解決問題的原理是什么。
                  <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>

                              哎呀哎呀视频在线观看