爲什麼強烈不推薦使用stop、suspend方法來中斷線程?

我們知道像stop、suspend這幾種中斷或者阻塞線程的方法在較高java版本中已經被標記上了@Deprecated過期標籤,那麼爲什麼她們曾經登上了java的歷史舞臺而又漸漸的推出了舞臺呢,到底是人性的扭曲還是道德的淪喪呢,亦或是她們不思進取被取而代之呢,如果是被取而代之,那麼取而代之的又是何方人也,本文我們將一探究竟。

一、stop的落幕

首先stop方法的作用是什麼呢,用java源碼中的一句註釋來了解一下:Forces the thread to stop executing.,即強制線程停止執行,'Forces’似乎已經透漏出了stop方法的蠻狠無理。那麼我們再看看java開發者是怎們解釋stop被淘汰了的: 在這裏插入圖片描述 我們從中可以看出以下幾點:

1.stop這種方法本質上是不安全的 2.使用Thread.stop停止線程會導致它解鎖所有已鎖定的監視器,即直接釋放當前線程已經獲取到的所有鎖,使得當前線程直接進入阻塞狀態

我們舉例來看一下上邊提到的兩點:

public static void main(String[] args) throws InterruptedException {
        Object o1=new Object();
        Object o2=new Object();
        Thread t1=new Thread(()->{
              synchronized (o1)
              {
                  synchronized (o2)
                  {
                      try {
                          System.out.println("t1獲取到鎖");
                          Thread.sleep(5000);
                          System.out.println("t1結束");
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
              }
        });
        t1.start();
        Thread.sleep(1000);
        Thread t2=new Thread(()->{
            synchronized (o1)
            {
                synchronized (o2)
                {
                    try {
                        System.out.println("t2獲取到鎖");
                        Thread.sleep(5000);
                        System.out.println("t2結束");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        t2.start();
        t1.stop();
    }

運行結果: 在這裏插入圖片描述 可以看到,當線程t1在獲取到o1和o2兩個鎖開始執行,在還沒有執行結束的時候,主線程調用了t1的stop方法中斷了t1的執行,釋放了t1線程獲取到的所有鎖,中斷後t2獲取到了o1和o2鎖,開始執行直到結束,而t1卻夭折在了sleep的時候,sleep後的代碼沒有執行。

因此使用stop我們在不知道線程到底運行到了什麼地方,暴力的中斷了線程,如果sleep後的代碼是資源釋放、重要業務邏輯等比較重要的代碼的話,亦或是其他線程依賴t1線程的運行結果,那直接中斷將可能造成很嚴重的後果。

那麼不建議使用stop中斷線程我們應該怎麼去優雅的結束一個線程呢,我們可以存java開發者的註釋中窺探到一種解決方案: 在這裏插入圖片描述 可以看到java開發者推薦我們使用以下兩種方法來優雅的停止線程:

1.定義一個變量,由目標線程去不斷的檢查變量的狀態,當變量達到某個狀態時停止線程。

代碼舉例如下:

volatile static boolean flag=false;
public static void main(String[] args) throws InterruptedException {
        Object o1=new Object();
        Thread t1=new Thread(()->{
              synchronized (o1)
              {
                  try {
                      System.out.println("t1獲取到鎖");
                      while (!flag)
                          Thread.sleep(5000);//執行業務邏輯
                      System.out.println("t1結束");
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
        });
        t1.start();
        Thread.sleep(1000);
        Thread t2=new Thread(()->{
            synchronized (o1)
            {
                try {
                    System.out.println("t2獲取到鎖");
                    Thread.sleep(5000);//執行業務邏輯
                    System.out.println("t2結束");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t2.start();
        flag=true;
    }

運行結果: 在這裏插入圖片描述 2.使用interrupt方法中斷線程。

代碼舉例如下:

public static void main(String[] args) throws InterruptedException {
        Object o1=new Object();
        Thread t1=new Thread(()->{
              synchronized (o1)
              {
                  System.out.println("t1獲取到鎖");
                  while (!Thread.currentThread().isInterrupted()) {
                      for (int i = 0; i < 100; i++) {
                          if(i==50)
                              System.out.println();
                          System.out.print(i+" ");
                      }
                      System.out.println();
                  }
                  System.out.println("t1結束");
              }
        });
        t1.start();
        Thread t2=new Thread(()->{
            synchronized (o1)
            {
                try {
                    System.out.println("t2獲取到鎖");
                    Thread.sleep(5000);//執行業務邏輯
                    System.out.println("t2結束");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t2.start();
        t1.interrupt();
    }

運行結果: 在這裏插入圖片描述 我們用while (!Thread.currentThread().isInterrupted())來不斷判斷當前線程是否被中斷,中斷的話則讓線程自然消亡並釋放鎖。可以看到調用interrupt方法後並不會像stop那樣暴力的中斷線程,會等到當前運行的邏輯結束後再檢查是否中斷,非常的優雅。

注:運行舉例代碼可能不會打印出數字,這是因爲t1線程運行到while(!Thread.currentThread().isInterrupted())時,主線程已經調了interrupt方法,因此多次運行可能會打印出數字。

二、suspend的落幕

suspend方法的作用是掛起某個線程直到調用resume方法來恢復該線程,但是調用了suspend方法後並不會釋放被掛起線程獲取到的鎖,正因如此就給suspend和resume這哥倆貼上了容易引發死鎖的標籤,當然這也正是導致suspend和resume退出歷史舞臺的罪魁禍首。同樣我們看看java開發者爲suspend的淘汰給出的理由: 在這裏插入圖片描述 從中我們可以得出以下結論:

1.suspend具有天然的死鎖傾向 2.當某個線程被suspend後,該線程持有的鎖不會被釋放,其他線程也就不能訪問這些資源 3.suspend某個線程後,如果在resume的過程中出現異常導致resume方法執行失敗,則lock無法釋放,導致死鎖

接下來模擬一下由suspend引起的死鎖場景,Talk is cheap,show my code:

public static void main(String[] args) throws InterruptedException {
        Object o1=new Object();
        Object o2=new Object();
        Thread t1=new Thread(()->{
              synchronized (o1)
              {
                  System.out.println("t1獲取到o1鎖開始執行");
                  try {
                      Thread.sleep(5000);//模擬執行業務邏輯
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  System.out.println("t1執行結束");
              }
        });
        t1.start();
        Thread t2=new Thread(()->{
            synchronized (o2)
            {
                System.out.println("t2獲取到o2開始執行");
                try {
                    Thread.sleep(2000);//執行耗時業務
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o1)
                {
                    System.out.println("t2獲取到o1鎖開始繼續執行");
                }
                System.out.println("t2執行結束");
            }
        });
        t2.start();

        Thread.sleep(1000);
        t1.suspend();
        //假設拋出了一個未知異常
        int i=1/0;
        t1.resume();
    }

運行結果: 在這裏插入圖片描述 可以看到,整個程序卡的死死的,在調用resume恢復t1線程之前拋出了一個未知異常,導致t1一直掛起進而無法釋放o1鎖,而t2需要獲取到o1鎖後才能繼續執行,但苦苦等待,奈何o1被t1拿捏的死死的,從此整個程序就陷入了無盡的等待中----死鎖。

作者:浪舟子 blog.csdn.net/qq_40400960/article/details/112651249

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章