日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java线程中断

發布時間:2023/12/3 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java线程中断 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

【README】

本文po出了不同場景下線程中斷的不同開發方式,包括阻塞,非阻塞,io阻塞線程等;

本文部分內容轉自:這篇博文寫的非常好

Thread的中斷機制(interrupt) - 寂靜沙灘 - 博客園先看收集了別人的文章,全面的了解下java的中斷:中斷線程線程的thread.interrupt()方法是中斷線程,將會設置該線程的中斷狀態位,即設置為true,中斷的結果線程是死亡、還是等待新的任務https://www.cnblogs.com/onlywujun/p/3565082.html


【1】使用thread.interrupt()中斷【非】阻塞狀態線程

子線程不調用 sleep,wait 等方法就不會進入阻塞狀態;

本文使用 while() 循環模擬 sleep睡眠方法, 如 tag1代碼;

public class Example02 extends Thread {public static void main(String[] args) throws InterruptedException {Example02 thread = new Example02();System.out.println("【主線程】starting thread");thread.start();TimeUnit.SECONDS.sleep(1);// 發出中斷請求thread.interrupt(); // tag0 System.out.println("【主線程】發出中斷請求后");TimeUnit.SECONDS.sleep(3);System.out.println("【主線程】隨眠3秒后");System.out.println("【主線程】stoping thread");}@Overridepublic void run() {while(!Thread.currentThread().isInterrupted()) { // tag2 long start = System.currentTimeMillis();while(System.currentTimeMillis() - start < 500); // tag1 System.out.println("【子線程】running");}System.out.println("【子線程】current thread exit.");} }

【代碼解說】

  • tag0代碼,調用 interrupt 之后,子線程的中斷狀態會設置為true;
  • 只要不調用阻塞方法,如 sleep,wait 等方法就不會拋出中斷異常;(即調用 thread.interrupt方法 是不會讓線程中斷的,僅僅是設置了中斷狀態而已)
  • 但tag2判斷為false(因為中斷狀態為true),所以子線程結束循環,正常退出;

【2】使用thread.interrupt()中斷阻塞狀態線程

線程調用 sleep, wait 方法都可以進入阻塞狀態;?

/*** @Description 使用 thread.interrupt() 中斷阻塞狀態線程,捕獲 InterruptedException 異常* @author xiao tang* @version 1.0.0* @createTime 2022年02月20日*/ public class Example02_2 extends Thread {public static void main(String[] args) throws InterruptedException {Example02_2 thread = new Example02_2();System.out.println("【主線程】starting thread");thread.start();TimeUnit.SECONDS.sleep(3);// 發出中斷請求thread.interrupt(); // tag0 System.out.println("【主線程】發出中斷請求后");TimeUnit.SECONDS.sleep(3);System.out.println("【主線程】隨眠3秒后");System.out.println("【主線程】stoping thread");}@Overridepublic void run() {while(!Thread.currentThread().isInterrupted()) { // tag1 try {TimeUnit.MILLISECONDS.sleep(500); // 可中斷式阻塞 tag2} catch (InterruptedException e) { // tag3 System.out.println("拋出中斷異常,線程中斷狀態=" + Thread.currentThread().isInterrupted()); // false }System.out.println("【子線程】running");}System.out.println("【子線程】current thread exit.");} }

【 代碼解說】

情況1: 主線程tag0代碼執行后, 設置線程中斷狀態為true;子線程立馬執行tag1,循環結束,子線程退出;(子線程被中斷,正常退出)

情況2:主線程tag0代碼執行后, 設置線程中斷狀態為true; 子線程 調用Thread.sleep(500) 會拋出中斷異常 InterruptedException(因為中斷狀態為true),而拋出InterruptedException異常后,java底層會把中斷標示位會自動清除(中斷狀態設置false);接著 while循環中的 !Thread.currentThread().isInterrupted() 為判斷為true,接著繼續執行循環體邏輯;(子線程被中斷,無法正常退出)

如何解決情況2中的子線程無法正常退出的情況?

在情況2的catch塊中調用 ?

Thread.currentThread().interrupt();//重新設置中斷狀態為true

重新設置中斷狀態為true ;然后while的判斷語句就會判斷為false,結束死循環,從而子線程才可以正常退出

當然,如果業務邏輯不需要讓子線程結束,即便遇到中斷的話,無需添加 Thread.currentThread().interrupt() 代碼;


【3】死鎖狀態線程無法被中斷

/*** @Description 死鎖狀態線程無法被中斷* @author xiao tang* @version 1.0.0* @createTime 2022年02月20日*/ public class Example03_DeadLock {static class MyLock {String name;MyLock(String name) {this.name = name;}}public static void main(String[] args) {final MyLock lock1 = new MyLock("鎖1");final MyLock lock2 = new MyLock("鎖2");// 線程1Thread t1 = new Thread(new Runnable() {public void run() {lock(lock1, lock2); // 傳入鎖的順序不一樣 }}, "線程1");// 線程2Thread t2 = new Thread(new Runnable() {public void run() {lock(lock2, lock1); // 傳入鎖的順序不一樣}}, "線程2");// 開啟線程t1.start();t2.start();// 中斷線程t1.interrupt(); // 設置線程1的中斷狀態為truet2.interrupt(); // 設置線程2的中斷狀態為trueSystem.out.println("【主線程】執行完成");}static void lock(MyLock lock1, MyLock lock2) {synchronized (lock1) { // 獲得鎖1System.out.println(Thread.currentThread().getName() + "獲得" + lock1.name);// 獲取鎖2前先睡眠3秒 以便線程2獲取鎖1,才能模擬出死鎖try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {}synchronized (lock2) { // 獲得鎖2System.out.println(Thread.currentThread().getName() + "獲得" + lock2.name);}}} }

線程1與線程2阻塞,程序無法正常結束;


【4】中斷IO阻塞的線程

/*** @Description 中斷IO阻塞線程* @author xiao tang* @version 1.0.0* @createTime 2022年02月20日*/ public class Eaxmple04_IOBlock extends Thread {volatile ServerSocket serverSocket; // 服務器套接字public static void main(String[] args) throws Exception {Eaxmple04_IOBlock ioBlockThread = new Eaxmple04_IOBlock();ioBlockThread.start();// 主線程睡眠等待子線程打開套接字TimeUnit.SECONDS.sleep(3);// 中斷子線程ioBlockThread.interrupt(); // 中斷 tat0 // ioBlockThread.serverSocket.close(); // tag3System.out.println("【主線程】關閉了服務器套接字");System.out.println("【主線程】結束");}@Overridepublic void run() {try {serverSocket = new ServerSocket(8080);} catch (IOException e) {System.out.println("【子線程】打開套接字失敗,直接返回");return ;}while(!Thread.currentThread().isInterrupted()) { // tag2try {System.out.println("【子線程】接收客戶端請求");serverSocket.accept();} catch (IOException e) { // 拋出IO異常后, java底層會把當前線程中斷狀態重置為false(清空)System.out.println("【子線程】accept()遇到io異常"); // Thread.currentThread().interrupt(); // 重新設置當前線程中斷狀態為true tag1} finally {try {serverSocket.close();System.out.println("【子線程】服務器套接字關閉");} catch (IOException e) {System.out.println("【子線程】服務器套接字關閉出現異常");}}}System.out.println("【子線程】io thread結束");} }

【注】注釋了 tag3? tag1 的代碼;

以上代碼的子線程無法正常結束;

服務器套接字 ServerSocket.accept() 沒有拋出 InterruptedException 中斷異常,所以它是非中斷式阻塞;所以 accept() 一直阻塞(子線程也一直阻塞),無法中斷;

解決方法:

添加 tag3代碼,主線程關閉 服務器套接字;

ioBlockThread.serverSocket.close(); // tag3

且 添加 tag1代碼 重新設置當前線程中斷狀態為true;如下:

Thread.currentThread().interrupt(); // 重新設置當前線程中斷狀態為true tag1

因為拋出 IOException狀態后,需要重新把當前線程中斷狀態設置為true,以便退出while循環,從而子線程才會正常結束;

注意:tag0的代碼? ioBlockThread.interrupt() 不能刪除,一旦刪除, 子線程將無法正常退出,可自行實驗

【4.1】如何中斷IO操作 (使用可中斷通道)

中斷I/O操作

然而,如果線程在I/O操作進行時被阻塞,又會如何?I/O操作可以阻塞線程一段相當長的時間,特別是牽扯到網絡應用時。例如,服務器可能需要等待一個請求(request),又或者,一個網絡應用程序可能要等待遠端主機的響應。

實現此InterruptibleChannel接口的通道是可中斷的:如果某個線程在可中斷通道上因調用某個阻塞的 I/O 操作(常見的操作一般有這些:serverSocketChannel. accept()、socketChannel.connect、socketChannel.open、socketChannel.read、socketChannel.write、fileChannel.read、fileChannel.write)而進入阻塞狀態,而另一個線程又調用了該阻塞線程的 interrupt 方法,這將導致該通道被關閉,并且已阻塞線程接將會收到ClosedByInterruptException,并且設置已阻塞線程的中斷狀態。另外,如果已設置某個線程的中斷狀態并且它在通道上調用某個阻塞的 I/O 操作,則該通道將關閉并且該線程立即接收到 ClosedByInterruptException;并仍然設置其中斷狀態。如果情況是這樣,其代碼的邏輯和第三個例子中的是一樣的,只是異常不同而已。

如果你正使用通道(channels)(這是在Java 1.4中引入的新的I/O API),那么被阻塞的線程將收到一個ClosedByInterruptException異常。但是,如果你使用Java1.0之前就存在的傳統的I/O,而且要求更多的工作。既然這樣,Thread.interrupt()將不起作用,因為線程將不會退出被阻塞狀態。盡管interrupt()被調用,線程也不會退出被阻塞狀態,比如ServerSocket的accept方法根本不拋出異常。

很幸運,Java平臺為這種情形提供了一項解決方案,即調用阻塞該線程的套接字的close()方法。在這種情形下,如果線程被I/O操作阻塞,當調用該套接字的close方法時,該線程在調用accept地方法將接收到一個SocketException(SocketException為IOException的子異常)異常,這與使用interrupt()方法引起一個InterruptedException異常被拋出非常相似,(注,如果是流因讀寫阻塞后,調用流的close方法也會被阻塞,根本不能調用,更不會拋IOExcepiton,此種情況下怎樣中斷?我想可以轉換為通道來操作流可以解決,比如文件通道)。下面是具體實現: 如? Eaxmple04_IOBlock


【5】小結

  • 一、沒有任何語言定義了一個被中斷的線程應該終止。中斷一個線程只是為了引起該線程的注意,被中斷線程可以決定如何應對中斷。
  • 二、對于處于sleep,join等操作的線程,如果被調用interrupt()后,會拋出InterruptedException,然后線程的中斷標志位會由true重置為false(只要拋出中斷異常,則中斷標識修改為false,即清空中斷標識),因為線程為了處理異常已經重新處于就緒狀態。
  • 三、不可中斷的操作,包括進入synchronized段以及Lock.lock(),inputSteam.read()等,調用interrupt()對于這幾個問題無效,因為它們都不拋出中斷異常。如果拿不到資源,它們會無限期阻塞下去。
  • 四:可中斷操作:對于Lock.lock(),可以改用Lock.lockInterruptibly(),可被中斷的加鎖操作,它可以拋出中斷異常。等同于等待時間無限長的Lock.tryLock(long time, TimeUnit unit)。對于inputStream等資源,有些(實現了 InterruptibleChannel 接口)可以通過close()方法將資源關閉,對應的阻塞也會被放開。

【5.1】Thread類里的幾個方法:

  • public static boolean?interrupted: 測試當前線程是否已經中斷。線程的中斷狀態?由該方法清除。換句話說,如果連續兩次調用該方法,則第二次調用將返回 false。
  • public boolean?isInterrupted():? 測試線程是否已經中斷。線程的中斷狀態?不受該方法的影響。
  • public void?interrupt(): 中斷線程。
  • 上面列出了與中斷有關的幾個方法及其行為,可以看到interrupt是中斷線程。如果不了解Java的中斷機制,這樣的一種解釋極容易造成誤解,認為調用了線程的interrupt方法就一定會中斷線程

    【5.2】其實,Java的中斷是一種協作機制。

    也就是說調用線程對象的interrupt方法并不一定就中斷了正在運行的線程,它只是要求線程自己在合適的時機中斷自己。每個線程都有一個boolean的中斷狀態(這個狀態不在Thread的屬性上),interrupt方法僅僅只是將該狀態置為true。比如對正常運行的線程調用interrupt()并不能終止他,只是改變了interrupt標示符。

    【5.3】如何判斷一個方法是否可中斷

    一般說來,如果一個方法聲明拋出InterruptedException,表示該方法是可中斷的,比如wait,sleep,join,也就是說可中斷方法會對interrupt調用做出響應(例如sleep響應interrupt的操作包括清除中斷狀態,拋出InterruptedException),異常都是由可中斷方法自己拋出來的,并不是直接由interrupt方法直接引起的。

    Object.wait, Thread.sleep方法,會不斷的輪詢監聽 interrupted 標志位,發現其設置為true后,會停止阻塞并拋出 InterruptedException異常。

    總結

    以上是生活随笔為你收集整理的java线程中断的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。