<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Java多線程死鎖問題

          共 13243字,需瀏覽 27分鐘

           ·

          2021-05-19 02:36

          點擊上方藍(lán)色字體,選擇“標(biāo)星公眾號”

          優(yōu)質(zhì)文章,第一時間送達(dá)

          死鎖問題

          死鎖定義

          多線程編程中,因為搶占資源造成了線程無限等待的情況,此情況稱為死鎖

          死鎖舉例

          注意:線程和鎖的關(guān)系是:一個線程可以擁有多把鎖,一個鎖只能被一個線程擁有。

          當(dāng)兩個線程分別擁有一把各自的鎖之后,又嘗試去獲取對方的鎖,這樣就會導(dǎo)致死鎖情況的發(fā)生,具體先看下面代碼:

          /**
           * 線程死鎖問題
           */
          public class DeadLock {
              public static void main(String[] args) {
                  //創(chuàng)建兩個鎖對象
                  Object lock1 = new Object();
                  Object lock2 = new Object();

                  //創(chuàng)建子線程
                  /*
                  線程1:①先獲得鎖1 ②休眠1s,讓線程2獲得鎖2 ③線程1嘗試獲取鎖2 線程2同理
                   */
                  Thread thread1 = new Thread(new Runnable() {
                      @Override
                      public void run() {
                          //線程1業(yè)務(wù)邏輯
                          synchronized(lock1){
                              System.out.println("線程1得到了鎖子1");
                              try {
                                  //休眠1s,讓線程2先得到鎖2
                                  Thread.sleep(1000);
                              } catch (InterruptedException e) {
                                  e.printStackTrace();
                              }
                              System.out.println("線程1嘗試獲取鎖2...");
                              synchronized(lock2){
                                  System.out.println("線程1獲得了鎖2!");
                              }
                          }
                      }
                  },"線程1");


                  Thread thread2 = new Thread(new Runnable() {
                      @Override
                      public void run() {
                          //線程2業(yè)務(wù)邏輯
                          synchronized(lock2){
                              System.out.println("線程2得到了鎖子2");
                              try {
                          //休眠1s,讓線程1先得到鎖1;因為線程是并發(fā)執(zhí)行我們不知道誰先執(zhí)行
                                  Thread.sleep(1000);
                              } catch (InterruptedException e) {
                                  e.printStackTrace();
                              }
                              System.out.println("線程2嘗試獲取鎖1...");
                              synchronized(lock1){
                                  System.out.println("線程2獲得了鎖1");
                              }
                          }
                      }
                  },"線程2");
                  thread1.start();
                  thread2.start();
              }
          }


          程序運行結(jié)果如下:

           ?可以看出,線程1嘗試獲取了鎖2,線程2嘗試獲取了鎖1,但是二者并沒有獲取到對方的鎖;這就發(fā)生了所謂的“死鎖”!

          如何排查死鎖

          想要排查死鎖具體細(xì)節(jié),可以通過三個工具(位于jdk安裝路徑bin目錄)去排查,現(xiàn)在就給大家介紹一下:

          1.jconsole


          可以看出,線程1和線程2發(fā)生了死鎖,死鎖發(fā)生的位置一目了然

          2.jvisualvm


          可以看出,發(fā)生了死鎖,線程1和線程2嘗試獲取的鎖是對方的鎖。

          3.jmc


          可以看出,同樣檢測出了死鎖情況
          無論是用哪個工具排查死鎖情況都是OK的。

          死鎖發(fā)生的條件

          1.互斥條件(一個鎖只能被一個線程占有,當(dāng)一個鎖被一個線程持有之后,不能再被其他線程持有);
          2.請求擁有(一個線程擁有一把鎖之后,又去嘗試請求擁有另外一把鎖);
          可以解決
          3.不可剝奪(一個鎖被一個線程占有之后,如果該線程沒有釋放鎖,其他線程不能強(qiáng)制獲得該鎖);
          4.環(huán)路等待條件(多線程獲取鎖時形成了一個環(huán)形鏈)
          可以解決

          怎么解決死鎖問題?

          環(huán)路等待條件相對于請求擁有更容易實現(xiàn),那么通過破壞環(huán)路等待條件解決死鎖問題
          破壞環(huán)路等待條件示意圖:

          針對于上面死鎖舉例中代碼,解決死鎖,具體看下面代碼:

          public class SolveDeadLock {
              public static void main(String[] args) {
                  //創(chuàng)建兩個鎖對象
                  Object lock1 = new Object();
                  Object lock2 = new Object();

                  Thread thread1 = new Thread(new Runnable() {
                      @Override
                      public void run() {
                          //線程1業(yè)務(wù)邏輯
                          synchronized(lock1){
                              System.out.println("線程1得到了鎖子1");
                              try {
                                  Thread.sleep(1000);
                              } catch (InterruptedException e) {
                                  e.printStackTrace();
                              }
                              System.out.println("線程1嘗試獲取鎖2...");
                              synchronized(lock2){
                                  System.out.println("線程1獲得了鎖2!");
                              }
                          }
                      }
                  },"線程1");

                  Thread thread2 = new Thread(new Runnable() {
                      @Override
                      public void run() {
                          //線程2業(yè)務(wù)邏輯
                          synchronized(lock1){
                              System.out.println("線程2得到了鎖子1");
                              try {
                                  Thread.sleep(1000);
                              } catch (InterruptedException e) {
                                  e.printStackTrace();
                              }
                              System.out.println("線程2嘗試獲取鎖2...");
                              synchronized(lock2){
                                  System.out.println("線程2獲得了鎖2");
                              }
                          }
                      }
                  },"線程2");
                  thread1.start();
                  thread2.start();
              }
          }


          程序運行結(jié)果如下:


          可以看出,通過破壞環(huán)路等待條件完美解決了死鎖問題

          線程通訊機(jī)制(wait/notify/notifyAll)

          定義

          線程通訊機(jī)制:一個線程的動作可以讓另外一個線程感知到,這就是線程通訊機(jī)制。
          wait():讓當(dāng)前線程進(jìn)入休眠等待狀態(tài);
          notify():喚醒當(dāng)前對象上的休眠等待線程;
          notifyAll():喚醒當(dāng)前對象上的所有休眠等待線程。

          相關(guān)面試重點

          面試問題:
          1.wait()使用時為什么需要加鎖?
          因為wait()必須在同步方法或者同步塊中使用,也就是說wait()需要配合加鎖一起使用(比如synchronized或Lock),調(diào)用對象調(diào)用wait()如果沒有適當(dāng)?shù)逆i,就會引發(fā)異常,因此說wait()使用時需要加鎖。
          2.wait()使用為什么要釋放鎖?
          wait()是Objetc類中一個實例方法,默認(rèn)是不傳任何值的,不傳值的時候表示讓當(dāng)前線程處于永久休眠等待狀態(tài),這樣會造成一個鎖被一個線程長時間一直擁有,為了避免這種問題的發(fā)生,使用wait()后必須釋放鎖。

          wait()/notify()/notifyAll()使用時注意事項:
          使用這三個方法時都必須進(jìn)行加鎖;
          2.加鎖的對象和調(diào)用wait()/notify()/notifyAll()對象必須是同一個對象;
          3.一組wait()/notify()/notifyAll()必須是同一個對象;
          4.notify()只能喚醒當(dāng)前對象上的一個休眠等到線程;而notifyAll()可以喚醒當(dāng)前對象上的所有休眠等待線程。

          sleep(0)和wait(0)的區(qū)別:
          1.sleep()是Thread類中一個靜態(tài)方法,wait()是Object類中一個普通的成員方法;
          2.sleep(0)會立即觸發(fā)一次CPU的搶占執(zhí)行,wait(0)會讓當(dāng)前線程無限休眠等待下去。

          wait()和sleep()的區(qū)別:
          相同點:
          1.都會讓當(dāng)前線程進(jìn)行休眠等待;
          2.使用二者時都需處理InterruptedException異常(try/catch)。
          不同點:
          1.wait()是Object中普通成員方法,sleep是Thread中靜態(tài)方法;
          2.wait()使用可以不穿參數(shù),sleep()必須傳入一個大于等于0的參數(shù);
          3.wait()使用時必須配合加鎖一起使用,sleep()使用時不需要加鎖;
          4.wait()使用時需要釋放鎖,如果sleep()加鎖后不會釋放鎖;
          5.wait()會讓當(dāng)前線程進(jìn)入WAITING狀態(tài)(默認(rèn)沒有明確的等待時間,當(dāng)被別的線程喚醒或者wait()傳參后超過等待時間量自己喚醒,將進(jìn)入就緒狀態(tài)),sleep()會讓當(dāng)前線程進(jìn)入TIMED_WAITING狀態(tài)(有明確的結(jié)束等待時間,但是這是死等的方式,休眠結(jié)束后進(jìn)入就緒狀態(tài))。

          *為什么wait()處于Object中而不是Thread中?(有點繞 我有點懵了…)
          wait()的調(diào)用必須進(jìn)行加鎖和釋放鎖操作,而鎖是屬于對象級別非線程級別,也就是說鎖針對于對象進(jìn)行操作而不是線程;而線程和鎖是一對多的關(guān)系,一個線程可以擁有多把鎖,而一個線程只能被一個線程擁有,為了靈活操作,就將wait()放在Object中。

          LockSupport

          LockSupport是對wait()的升級,無需加鎖也無需釋放鎖;

          • LockSupport.park()讓線程休眠,和wait()一樣會讓線程進(jìn)入WAITING狀態(tài);

          • LockSupport.unpark()喚醒線程,可以喚醒對象上指定的休眠等待線程;(優(yōu)勢)

          LockSupport與wait()區(qū)別

          wait()與LockSupport的區(qū)別:

          相同點:
          1.二者都可以讓線程進(jìn)入休眠等待狀態(tài);
          2.二者都可以傳參或者不傳參,讓線程都會進(jìn)入到WAITING狀態(tài)。
          不同點:
          1.wait()需要配合加鎖一起使用,LockSupport無需加鎖;
          2.wait()只能喚醒對象的隨機(jī)休眠線程和全部線程,LockSupport可以喚醒對象的指定休眠線程。



          版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接和本聲明。

          本文鏈接:

          https://blog.csdn.net/weixin_44874269/article/details/116639326





          鋒哥最新SpringCloud分布式電商秒殺課程發(fā)布

          ??????

          ??長按上方微信二維碼 2 秒





          感謝點贊支持下哈 

          瀏覽 49
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产精品你懂的在线观看 | 一级特黄大片色 | 韩国三级电影HD中文久久免费 | wwwwwww黄色片 | 亚洲无码高清在线视频 |