Java:一個重要的停止線程方法——interrupt

2021-02-19 BHS編程技術交流分享

一、前言

之前本人寫了一篇防止Controller中的線程被重複調用的文章,大概代碼如下:

public final static HashMap<String,Thread> threadMap = new HashMap<>();Thread nowThread = threadMap.get("nowThread");if(nowThread != null && nowThread.isAlive()){    LOG.info("當前存在正在執行的線程,本次線程不執行,請稍後再試");    return;}
else{ threadMap.put("nowThread",Thread.currentThread());}.
threadMap.put("nowThread",null);

後來,由於擔心這個線程會卡死,導致後續正常調用該線程的操作無法進行,因此加了個手動停止線程運行的方法(interrupt):

if(type == 3){ try{   Thread nowThread = threadMap.get("nowThread");      nowThread.interrupt();   threadMap.put("nowThread",null);   String backMsg = "線程interrupt成功!";   LOG.info(backMsg);   return; }catch(Exception e){   threadMap.put("nowThread",null);   String backMsg = "線程interrupt失敗,只將map置空!";   LOG.error(backMsg,e);   return; }}

然而,僅僅這樣並不能停止線程運行。

二、停止線程運行的方法

使用標誌位停止線程,還行,但是遇到sleep時的線程就無法停止了,必須等線程sleep結束、運行到判斷標誌位步驟時才行。

使用stop()停止線程,不安全,已不推薦使用。會立即停止線程運行,會使一些清理性的工作的得不到完成,如文件,資料庫等的關閉;會立即釋放該線程所持有的所有的鎖,導致數據得不到同步,出現數據不一致的問題。

使用interrupt()中斷線程,需要配合代碼邏輯實現停止線程。

需要注意,調用 interrupt() 方法僅僅是在當前線程中打一個停止的標記,並不是真的停止線程;如果直接將線程停止,就會和stop一樣出現問題。

因此還需要配合代碼邏輯實現。

三、正確使用interrupt停止線程運行

先看下例子:

public static void main(String[] args) throws InterruptedException{  Thread t1 = new Thread(){    @Override    public void run(){            while(true){                if(this.currentThread().isInterrupted()){          System.out.println("線程被打斷,停止運行");          break;        }else{          System.out.println("線程在運行");          try{                        Thread.sleep(1000L);          }catch(InterruptedException e){                        System.out.println("線程睡眠時被打斷,停止運行");            break;          }        }      }    }  };  t1.start();  Thread.sleep(5000L);  t1.interrupt();  System.out.println("主線程打斷t1完成");}

上方的代碼中,先啟動一個線程,然後主線程在5秒後打斷它,它有兩種停止情況:

執行if判斷時,發現標誌位為true,因此執行break,之後停止運行。

線程還在sleep,此時發現被打斷,會拋出InterruptedException,然後被catch到,執行break,之後停止運行。

結合實際代碼,如果線程中不需要sleep,則判斷isInterrupted()即可;

如果線程中存在sleep,則catch中使用break、return之類的停止線程即可;

當然也可以兩種都寫。

本人的代碼中有sleep,因此只在catch中加了break,沒有判斷標誌位。

四、總結

使用interrupt停止線程比標誌位停止線程的好處在於,它不僅能通過標誌判斷是否要停止線程,而且當線程處於sleep狀態時,使用interrupt就可以停止線程,而標誌位不行。

使用interrupt停止線程比stop停止線程的好處在於,stop不安全,會產生難以預料的後果,而interrupt不會。

停止線程時,不僅要直接調用interrupt(),還要寫好相應的代碼邏輯,一種邏輯與標誌位停止線程類似,另一種邏輯要注意在try中寫Thread.sleep(當你的線程需要sleep時),在catch中寫break、return之類的方法。

PS:之前寫sleep時,都是這麼寫的:

try{ Thread.sleep(5000L);}catch(Exception e){}

現在終於知道應該怎麼寫了

相關焦點

  • 騰訊面試官:如何停止一個正在運行的線程?我一臉蒙蔽...
    停止一個線程意味著在任務處理完任務之前停掉正在做的操作,也就是放棄當前的操作。停止一個線程可以用Thread.stop()方法,但最好不要用它。雖然它確實可以停止一個正在運行的線程,但是這個方法是不安全的,而且是已被廢棄的方法。
  • 大數據基礎:Java多線程入門
    在大數據開發學習當中,Java基礎是非常重要的一部分,打好了Java基礎,才能在後續的大數據框架技術學習階段,也能有所主力。而Java當中的一個重要知識點,就是多線程。今天的大數據基礎分享,我們就主要來講講Java多線程入門基礎。
  • 一個神奇的bug:OOM?優雅終止線程?系統內存佔用較高?
    在修改上面提到OOM那個bug並通過測試後,測試同學發現我們的作業無法停止下來,換句話說,我們作業所在的java線程無法停止。線程停止失敗重現一番debug與代碼深入研讀之後,發現我們項目中確實是調用了對應的線程對象的interrupt方法thread.interrupt();去終止線程的。
  • Java面試題-多線程篇十三
    兩種方式:java.lang.Thread 類的實例就是一個線程但是它需要調用java.lang.Runnable接口來執行,由於線程類本身就是調用的Runnable接口所以你可以繼承java.lang.Thread 類或者直接調用Runnable接口來重寫run()方法實現線程。
  • Java多線程編程必備基礎知識
    :上圖中基本上囊括了 Java 中多線程各重要知識點。這個時候需要關閉其他線程;很多線程的運行模式是死循環,比如在生產者/消費者模式中,消費者主體就是一個死循環,它不停的從隊列中接受任務,執行任務,在停止程序時,我們需要一種」優雅」的方法以關閉該線程;在一些場景中,比如從第三方伺服器查詢一個結果,我們希望在限定的時間內得到結果,如果得不到,我們會希望取消該任務
  • 創建多少線程是合適的
    使用多線程是主要目的就是提高性能,而性能的指標有很多,最主要的性能指標就是延遲和吞吐量延遲越低,吞吐量就越高,由於他們隸屬於不同維度,一個是時間維度,一個是空間維度,因此不能相互轉換提升性能就要是兩個方向,一個就是優化算法,一個就是提高硬體的性能能發揮到機制,我使用多線程就是要把硬體的性能提高到機制,而硬體的主要分兩類一類就是io,一個就是cpu,在並發編程領域中,提升性能就是提高硬體的利用率
  • 最全面的Java多線程用法解析
    最全面的java多線程用法解析,如果你對Java的多線程機制並沒有深入的研究,那麼本文可以幫助你更透徹地理解Java多線程的原理以及使用方法。1.創建線程在Java中創建線程有兩種方法:使用Thread類和使用Runnable接口。在使用Runnable接口時需要建立一個Thread實例。
  • Java中,ThreadGroup的interrupt方法源碼,難道你還不知道嗎?
    interupt一個thread group會導致該group中所有的active線程都被interrupt,也就是說該group中每一個線程的interrupt標識都被設置了,下面是ThreadGroup的interrupt方法的源碼:public final void
  • JAVA如何優雅地終止線程?
    錯誤的線程中止 - stop首先來講解一個錯誤的方式來終止線程 — stop:中止線程,並且清除監控器鎖的信息,但是可能導致線程安全問題,JDK 不建議使用,類似的方法還有 destory,由於 JDK 從未實現該方法,在這裡就不介紹了。
  • Java 線程面試題 Top 50
    Java語言一個重要的特點就是內置了對並發的支持,讓Java大受企業和程式設計師的歡迎。大多數待遇豐厚的Java開發職位都要求開發者精通多線程技術並且有豐富的Java程序開發、調試、優化經驗,所以線程相關的問題在面試中經常會被提到。
  • Java編程中,interrupt的注意事項,看完你就明白了
    主要用來控制是否擦除線程interrupt的標識。isInterrupted方法的源碼中該參數為false,表示不想擦除:public boolean isInterrupted(){return isInterrupted(false);}如果一個線程在沒有執行可中斷方法之前就被打斷,那麼其接下來將執行可中斷方法,比如sleep會發生什麼樣的情況呢?
  • 面試前必看Java線程面試題
    java.lang.Thread 類的實例就是一個線程但是它需要調用java.lang.Runnable接口來執行,由於線程類本身就是調用的Runnable接口所以你可以繼承java.lang.Thread 類或者直接調用Runnable接口來重寫run()方法實現線程。更多詳細信息請點擊這裡。5. 什麼是線程安全?Vector是一個線程安全類嗎?
  • Java Thread類簡述
    3、Thread類中的方法 java.lang.Thread類的源碼: 下面是Thread類中常用的關係到線程運行狀態的方法: 1)start方法 start()用來啟動一個線程,當調用start方法後,系統才會開啟一個新的線程來執行用戶定義的子任務,在這個過程中,會為相應的線程分配需要的資源。
  • java面試系列--J2SE基礎(十二)
    wait是java.lang包的,屬於Object的方法,sleep是屬於Thread的方法;wait的時候,當前線程會釋放資源鎖,然後進入等待鎖定池,只有針對此對象調用notify方法後本線程才會繼續執行,sleep的時候,當前資源鎖並沒有釋放,直到sleep結束然後繼續執行;sleep的時候一般都要對異常進行處理,捕捉或外拋,而且sleep方法是一個靜態方法
  • java的線程創建方式
    Thread類java語言中的Thread類是一個基本的線程類,用於創建線程、中斷線程、獲取線程的基本信息、運行狀態等。我們首先了解下利用Thread類創建線程實例的二種方式。繼承Thread類創建線程//繼承Thread實現自己的線程類class MyThread extends Thread{//重寫run方法,給線程賦予工作任務 @Override public void run() { //任務內容…… System.out.println("當前線程是:"+Thread.currentThread
  • 初學Java多線程:向線程傳遞數據的三種方法
    初學Java多線程:向線程傳遞數據的三種方法 本文講述在學習Java多線程中需要學習的向線程傳遞數據的三種方法。由於線程的運行和結束是不可預料的,因此,在傳遞和返回數據時就無法象函數一樣通過函數參數和return語句來返回數據。
  • 線程不是你想中斷就能中斷
    為什麼不強制停止 如何用 interrupt 停止線程 sleep 期間能否感受到中斷 停止線程的方式有幾種 總結啟動線程需要調用 Thread 類的 start() 方法,並在 run() 方法中定義需要執行的任務。啟動一個線程非常簡單,但如果想要正確停止它就沒那麼容易了。
  • JAVA多線程 集合同步
    經典做法在迭代開始之前,使用synchronized手動同步所有針對List的修改操作(增加、刪除),保證迭代期間沒有線程修改List。此方法需要明確各個方法的同步,考慮的因素很多,不建議使用。B.休眠時間結束後thread-1進入運行狀態去執行synchronizedArrayList的add(i)方法,但由於synchronizedArrayList已被synchronized限制(你必須清楚的是只有返回的迭代器是非線程安全外,synchronizedArrayList其餘的所有方法都是完全的線程安全的), 因此thread-1必須等待thread-2釋放synchronizedArrayList
  • Java經典面試題:一個線程兩次調用start()方法會出現什麼情況?
    今天我要問你的問題是,一個線程兩次調用start()方法會出現什麼情況?談談線程的生命周期和狀態轉移。典型回答Java的線程是不允許啟動兩次的,第二次調用必然會拋出IllegalThreadStateException,這是一種運行時異常,多次調用start被認為是編程錯誤。
  • Java編寫線程安全類的7個技巧
    一、無狀態(No State)當多個線程訪問相同的實例或靜態變量時,必須以某種方式來協調對此變量的訪問。最簡單的方法就是避免使用實例或靜態變量。對於沒有實例變量的類,它的方法只使用局部變量和方法參數。以下示例顯示了java.lang.Math類的其中一部分:二、無共享狀態(No Shared State)如果你必須要使用狀態,那麼請不要共享狀態,即狀態應該只屬於一個單一的線程。這種技術的一個例子是SWT或Swing圖形用戶界面框架的事件處理線程。您可以通過擴展Thread類並添加實例變量來實現「本地線程」(thread-local)實例變量。