停止Java線程,小心interrupt()方法

轉自http://www.blogjava.NET/jinfeng_wang/archive/2008/04/27/196477.html?

----------------------------------------------------------------------------------------------------


程序是很簡易的。然而,在編程人員面前,多線程呈現出了一組新的難題,如果沒有被恰當的解決,將導致意外的行為以及細微的、難以發現的錯誤。
  在本篇文章中,我們針對這些難題之一:如何中斷一個正在運行的線程。?

背景?
??? 中斷(Interrupt)一個線程意味著在該線程完成任務之前停止其正在進行的一切,有效地中止其當前的操作。線程是死亡、還是等待新的任務或是繼續運行至下一步,就取決于這個程序。雖然初次看來它可能顯得簡單,但是,你必須進行一些預警以實現期望的結果。你最好還是牢記以下的幾點告誡。?

??? 首先,忘掉Thread.stop方法。雖然它確實停止了一個正在運行的線程,然而,這種方法是不安全也是不受提倡的,這意味著,在未來的Java版本中,它將不復存在。?

??? 一些輕率的家伙可能被另一種方法Thread.interrupt所迷惑。盡管,其名稱似乎在暗示著什么,然而,這種方法并不會中斷一個正在運行的線程(待會將進一步說明),正如Listing A中描述的那樣。它創建了一個線程,并且試圖使用Thread.interrupt方法停止該線程。Thread.sleep()方法的調用,為線程的初始化和中止提供了充裕的時間。線程本身并不參與任何有用的操作。?
Listing A?

[java]?view plaincopy
  1. class?Example1?extends?Thread?{??
  2. ????????????boolean?stop=false;??
  3. ????????????public?static?void?main(?String?args[]?)?throws?Exception?{??
  4. ????????????Example1?thread?=?new?Example1();??
  5. ????????????System.out.println(?"Starting?thread..."?);??
  6. ????????????thread.start();??
  7. ????????????Thread.sleep(?3000?);??
  8. ????????????System.out.println(?"Interrupting?thread..."?);??
  9. ????????????thread.interrupt();??
  10. ????????????Thread.sleep(?3000?);??
  11. ????????????System.out.println("Stopping?application..."?);??
  12. ????????????//System.exit(0);??
  13. ????????????}??
  14. ????????????public?void?run()?{??
  15. ????????????while(!stop){??
  16. ????????????System.out.println(?"Thread?is?running..."?);??
  17. ????????????long?time?=?System.currentTimeMillis();??
  18. ????????????while((System.currentTimeMillis()-time?<?1000))?{??
  19. ????????????}??
  20. ????????????}??
  21. ????????????System.out.println("Thread?exiting?under?request..."?);??
  22. ????????????}??
  23. ????????????}??
如果你運行了Listing A中的代碼,你將在控制臺看到以下輸出:?

Starting thread...?

Thread is running...?

Thread is running...?

Thread is running...?

Interrupting thread...?

Thread is running...?

Thread is running...?

Thread is running...?

Stopping application...?

Thread is running...?

Thread is running...?

Thread is running...?
...............................?
甚至,在Thread.interrupt()被調用后,線程仍然繼續運行。?

真正地中斷一個線程?

??? 中斷線程最好的,最受推薦的方式是,使用共享變量(shared variable)發出信號,告訴線程必須停止正在運行的任務。線程必須周期性的核查這一變量(尤其在冗余操作期間),然后有秩序地中止任務。Listing B描述了這一方式。?

Listing B?
[java]?view plaincopy
  1. class?Example2?extends?Thread?{??
  2. ??volatile?boolean?stop?=?false;??
  3. ??public?static?void?main(?String?args[]?)?throws?Exception?{??
  4. ???Example2?thread?=?new?Example2();??
  5. ???System.out.println(?"Starting?thread..."?);??
  6. ???thread.start();??
  7. ???Thread.sleep(?3000?);??
  8. ???System.out.println(?"Asking?thread?to?stop..."?);??
  9. ??
  10. ???thread.stop?=?true;??
  11. ???Thread.sleep(?3000?);??
  12. ???System.out.println(?"Stopping?application..."?);??
  13. ???//System.exit(?0?);??
  14. ??}??
  15. ??
  16. ??public?void?run()?{??
  17. ????while?(?!stop?)?{??
  18. ?????System.out.println(?"Thread?is?running..."?);??
  19. ??????long?time?=?System.currentTimeMillis();??
  20. ??????while?(?(System.currentTimeMillis()-time?<?1000)?&&?(!stop)?)?{??
  21. ??????}??
  22. ????}??
  23. ???System.out.println(?"Thread?exiting?under?request..."?);??
  24. ??}??
  25. }??

運行Listing B中的代碼將產生如下輸出(注意線程是如何有秩序的退出的)?

Starting thread...?

Thread is running...?

Thread is running...?

Thread is running...?

Asking thread to stop...?

Thread exiting under request...?

Stopping application...?

?? 雖然該方法要求一些編碼,但并不難實現。同時,它給予線程機會進行必要的清理工作,這在任何一個多線程應用程序中都是絕對需要的。請確認將共享變量定義成volatile 類型或將對它的一切訪問封入同步的塊/方法(synchronized blocks/methods)中。?

到目前為止一切順利!但是,當線程等待某些事件發生而被阻塞,又會發生什么?當然,如果線程被阻塞,它便不能核查共享變量,也就不能停止。這在許多情況下會發生,例如調用Object.wait()、ServerSocket.accept()和DatagramSocket.receive()時,這里僅舉出一些。?

他們都可能永久的阻塞線程。即使發生超時,在超時期滿之前持續等待也是不可行和不適當的,所以,要使用某種機制使得線程更早地退出被阻塞的狀態。?

很不幸運,不存在這樣一種機制對所有的情況都適用,但是,根據情況不同卻可以使用特定的技術。在下面的環節,我將解答一下最普遍的例子。?

使用Thread.interrupt()中斷線程?

? 正如Listing A中所描述的,Thread.interrupt()方法不會中斷一個正在運行的線程。這一方法實際上完成的是,在線程受到阻塞時拋出一個中斷信號,這樣線程就得以退出阻塞的狀態。更確切的說,如果線程被Object.wait, Thread.join和Thread.sleep三種方法之一阻塞,那么,它將接收到一個中斷異常(InterruptedException),從而提早地終結被阻塞狀態。?

??? 因此,如果線程被上述幾種方法阻塞,正確的停止線程方式是設置共享變量,并調用interrupt()(注意變量應該先設置)。如果線程沒有被阻塞,這時調用interrupt()將不起作用;否則,線程就將得到異常(該線程必須事先預備好處理此狀況),接著逃離阻塞狀態。在任何一種情況中,最后線程都將檢查共享變量然后再停止。Listing C這個示例描述了該技術。?

Listing C?

[java]?view plaincopy
  1. class?Example3?extends?Thread?{??
  2. ??volatile?boolean?stop?=?false;??
  3. ??public?static?void?main(?String?args[]?)?throws?Exception?{??
  4. ???Example3?thread?=?new?Example3();??
  5. ???System.out.println(?"Starting?thread..."?);??
  6. ???thread.start();??
  7. ???Thread.sleep(?3000?);??
  8. ???System.out.println(?"Asking?thread?to?stop..."?);??
  9. ???thread.stop?=?true;//如果線程阻塞,將不會檢查此變量??
  10. ???thread.interrupt();??
  11. ???Thread.sleep(?3000?);??
  12. ???System.out.println(?"Stopping?application..."?);??
  13. ???//System.exit(?0?);??
  14. ??}??
  15. ??
  16. ??public?void?run()?{??
  17. ????while?(?!stop?)?{??
  18. ?????System.out.println(?"Thread?running..."?);??
  19. ??????try?{??
  20. ??????Thread.sleep(?1000?);??
  21. ??????}?catch?(?InterruptedException?e?)?{??
  22. ??????System.out.println(?"Thread?interrupted..."?);??
  23. ??????}??
  24. ????}??
  25. ???System.out.println(?"Thread?exiting?under?request..."?);??
  26. ??}??
  27. }??

一旦Listing C中的Thread.interrupt()被調用,線程便收到一個異常,于是逃離了阻塞狀態并確定應該停止。運行以上代碼將得到下面的輸出:?

Starting thread...?

Thread running...?

Thread running...?

Thread running...?

Asking thread to stop...?

Thread interrupted...?

Thread exiting under request...?

Stopping application...?


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

如果你正使用通道(channels)(這是在Java 1.4中引入的新的I/O API),那么被阻塞的線程將收到一個ClosedByInterruptException異常。如果情況是這樣,其代碼的邏輯和第三個例子中的是一樣的,只是異常不同而已。?

但是,你可能正使用Java1.0之前就存在的傳統的I/O,而且要求更多的工作。既然這樣,Thread.interrupt()將不起作用,因為線程將不會退出被阻塞狀態。Listing D描述了這一行為。盡管interrupt()被調用,線程也不會退出被阻塞狀態?

Listing D?

[java]?view plaincopy
  1. import?java.io.*;??
  2. class?Example4?extends?Thread?{??
  3. ??public?static?void?main(?String?args[]?)?throws?Exception?{??
  4. ????Example4?thread?=?new?Example4();??
  5. ???System.out.println(?"Starting?thread..."?);??
  6. ???thread.start();??
  7. ???Thread.sleep(?3000?);??
  8. ???System.out.println(?"Interrupting?thread..."?);??
  9. ???thread.interrupt();??
  10. ???Thread.sleep(?3000?);??
  11. ???System.out.println(?"Stopping?application..."?);??
  12. ???//System.exit(?0?);??
  13. ??}??
  14. ??
  15. ??public?void?run()?{??
  16. ???ServerSocket?socket;??
  17. ????try?{??
  18. ??????socket?=?new?ServerSocket(7856);??
  19. ????}?catch?(?IOException?e?)?{??
  20. ?????System.out.println(?"Could?not?create?the?socket..."?);??
  21. ??????return;??
  22. ????}??
  23. ????while?(?true?)?{??
  24. ?????System.out.println(?"Waiting?for?connection..."?);??
  25. ??????try?{??
  26. ???????Socket?sock?=?socket.accept();??
  27. ??????}?catch?(?IOException?e?)?{??
  28. ??????System.out.println(?"accept()?failed?or?interrupted..."?);??
  29. ??????}??
  30. ????}??
  31. ??}??
  32. }??

?很幸運,Java平臺為這種情形提供了一項解決方案,即調用阻塞該線程的套接字的close()方法。在這種情形下,如果線程被I/O操作阻塞,該線程將接收到一個SocketException異常,這與使用interrupt()方法引起一個InterruptedException異常被拋出非常相似。?

唯一要說明的是,必須存在socket的引用(reference),只有這樣close()方法才能被調用。這意味著socket對象必須被共享。Listing E描述了這一情形。運行邏輯和以前的示例是相同的。?

Listing E?

[java]?view plaincopy
  1. import?java.net.*;??
  2. import?java.io.*;??
  3. class?Example5?extends?Thread?{??
  4. ??volatile?boolean?stop?=?false;??
  5. ??volatile?ServerSocket?socket;??
  6. ??public?static?void?main(?String?args[]?)?throws?Exception?{??
  7. ????Example5?thread?=?new?Example5();??
  8. ???System.out.println(?"Starting?thread..."?);??
  9. ???thread.start();??
  10. ???Thread.sleep(?3000?);??
  11. ???System.out.println(?"Asking?thread?to?stop..."?);??
  12. ???thread.stop?=?true;??
  13. ???thread.socket.close();??
  14. ???Thread.sleep(?3000?);??
  15. ???System.out.println(?"Stopping?application..."?);??
  16. ???//System.exit(?0?);??
  17. ??}??
  18. ??public?void?run()?{??
  19. ????try?{??
  20. ??????socket?=?new?ServerSocket(7856);??
  21. ????}?catch?(?IOException?e?)?{??
  22. ?????System.out.println(?"Could?not?create?the?socket..."?);??
  23. ??????return;??
  24. ????}??
  25. ????while?(?!stop?)?{??
  26. ?????System.out.println(?"Waiting?for?connection..."?);??
  27. ??????try?{??
  28. ???????Socket?sock?=?socket.accept();??
  29. ??????}?catch?(?IOException?e?)?{??
  30. ??????System.out.println(?"accept()?failed?or?interrupted..."?);??
  31. ??????}??
  32. ????}??
  33. ???System.out.println(?"Thread?exiting?under?request..."?);??
  34. ??}??
  35. }??

以下是運行Listing E中代碼后的輸出:?

Starting thread...?

Waiting for connection...?

Asking thread to stop...?

accept() failed or interrupted...?

Thread exiting under request...?

Stopping application...?

多線程是一個強大的工具,然而它正呈現出一系列難題。其中之一是如何中斷一個正在運行的線程。如果恰當地實現,使用上述技術中斷線程將比使用Java平臺上已經提供的內嵌操作更為簡單。


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/538991.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/538991.shtml
英文地址,請注明出處:http://en.pswp.cn/news/538991.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

python輸入數學表達式并求值_用Python3實現表達式求值

一、題目描述 請用 python3編寫一個計算器的控制臺程序&#xff0c;支持加減乘除、乘方、括號、小數點&#xff0c;運算符優先級為括號>乘方>乘除>加減&#xff0c;同級別運算按照從左向右的順序計算。 二、輸入描述 數字包括"0123456789"&#xff0c;小數點…

mac上的mysql管理工具sequel pro

https://blog.csdn.net/wan_zaiyunduan/article/details/54909389 以前用過Plsql、Navicat、Workbench&#xff0c;現在換到mac上&#xff0c;用了現在這一款管理工具&#xff0c;很好用&#xff0c;所以推薦給大家。 完整的MySQL支持 Sequel Pro是一個快速,易于使用的Mac數據庫…

報錯 classes 拒絕訪問_3種方式“移除”快速訪問;為什么移除?你懂的...

Windows 10 在文件資源管理器中引入了"快速訪問"這個功能&#xff0c;每當打開文件資源管理器窗口時&#xff0c;您都會看到常用文件夾和最近訪問的文件的列表&#xff0c;這個功能雖然方便了日常使用&#xff0c;可能會提高工作效率&#xff0c;但是如果是公司的電腦…

java set是重復_java算法題,set內出現重復元素

題目將數字 1…9 填入一個33 的九宮格中&#xff0c;使得格子中每一橫行和的值全部相等&#xff0c;每一豎列和的值全部相等。請你計算有多少種填數字的方案。這個是計蒜客上面的一個模擬題&#xff0c;我采用暴力。public class _3 {/** 將數字 1…9 填入一個33 的九宮格中&am…

Lock的lockInterruptibly()

概述 lockInterruptibly()方法比較特殊&#xff0c;當通過這個方法去獲取鎖時&#xff0c;如果其他線程正在等待獲取鎖&#xff0c;則這個線程能夠響應中斷&#xff0c;即中斷線程的等待狀態。也就使說&#xff0c;當兩個線程同時通過lock.lockInterruptibly()想獲取某個鎖時&…

python中把輸出結果寫到一個文件中_Python3.6筆記之將程序運行結果輸出到文件的方法...

Python3.6筆記之將程序運行結果輸出到文件的方法 更新時間&#xff1a;2018年04月22日 14:27:32 投稿&#xff1a;jingxian 下面小編就為大家分享一篇Python3.6筆記之將程序運行結果輸出到文件的方法&#xff0c;具有很好的參考價值&#xff0c;希望對大家有所幫助。一起跟隨小…

hangfire.mysql.core_abp 使用 hangfire結合mysql

abp 官方使用的hangfire 默認使用的是sqlserver的存儲mysql須要引入支持mysql的類庫sql我這邊使用的是Hangfire.MySql.Core數據庫直接用nuget安裝便可app首先按照官方文檔要求&#xff0c;改幾個地方sqlserver分別是 Startup 文件下serverservices.AddHangfire(config >{con…

python 圖標題上移_Python-Matplotlib將圖形標題移動到y軸

我目前在python中使用matplotlib來繪制一些數據,但是我希望圖表的標題位于Y軸上,因為沒有足夠的空間來存儲一個圖形的標題和另一個圖形的x軸標簽.我知道我可以將hspace設置為更大的數字但是,我不想這樣做,因為我計劃將幾個圖表堆疊在一起,如果我調整hspace,那么圖表將是真的簡短…

solr的基礎使用

查詢運算符 例如&#xff1a;http://localhost:8984/solr/mycore/select?q*:* : 指定字段查指定值&#xff0c;如返回所有值q*:* ? 匹配單個字符&#xff0c; 例如: qtitle:??拳 可以匹配標題為“形意拳”的文檔 * 匹配零個或多個字符, 例如: qtitle:*形意拳 或者 qtitl…

同步關鍵詞lock

概述 1、API在JDK的java.util.concurrent.locks下。 2、不是Java關鍵字&#xff0c;是接口。 3、ReentrantLock是JDK唯一實現了Lock接口的類。 public interface Lock {//獲取鎖void lock();//可以響應中斷的鎖void lockInterruptibly() throws InterruptedException;//嘗試…

Java bitset轉string_將java BitSet保存到DB

默認情況下,JPA使用Java序列化來保存未知Serializable類型的屬性(以便將序列化表示存儲為byte []).通常它不是您想要的,因為可以有更有效的方式來表示您的數據.例如,BitSet可以有效地表示為數字(如果它的大小有界),或者byte [],或其他東西(遺憾的是,BitSet不提供進行這些轉換的…

python讀取raw圖片文件_在python下讀取并展示raw格式的圖片實例

raw文件可能有些人沒有&#xff0c;因此&#xff0c;先用一張圖片創建一個raw格式的文件&#xff08;其實可以是其他類型的格式文件&#xff09; import numpy as np import cv2 img cv2.imread(cat.jpg) # 這里需要我們在當前目錄下放一張名為cat.jpg的文件 img.tofile(cat.r…

python怎么網絡通信_深入Python中的網絡通信

TCP/IP計算機與網絡設備兩情侶要談戀愛&#xff0c;相互通信&#xff0c;那么雙方就必須有規則。基于相同的方法&#xff0c;不同的硬件、操作系統之間的通信&#xff0c;都需要一種規則。而我們就把這種規則稱為協議(protocol)。TCP/IP 是互聯網相關各類協議族的總稱。TCP/IP是…

ReadWriteLock讀寫文件

概述 ReadWriteLock是一個接口&#xff0c;在它里面只定義了兩個方法&#xff1a;一個讀的鎖和一個寫的鎖。 讀的鎖&#xff1a;A線程獲取了讀的鎖&#xff0c;那么B線程也可以獲取讀的鎖。 寫的鎖&#xff1a;A線程獲取了寫的鎖&#xff0c;那么B線程不能獲取讀也不能獲取寫…

搞懂 Java HashMap 源碼

HashMap 源碼分析 前幾篇分析了 ArrayList &#xff0c; LinkedList &#xff0c;Vector &#xff0c;Stack List 集合的源碼&#xff0c;Java 容器除了包含 List 集合外還包含著 Set 和 Map 兩個重要的集合類型。而 HashMap 則是最具有代表性的&#xff0c;也是我們最常使用到…

python 怎么表示sqlserver null_如何使用Python將sqlserver查詢輸出寫入.txt文件?

我是Python新手&#xff0c;嘗試連接到sqlserverdb并將查詢的輸出轉換成一個flat.txt文件。在一些代碼正在工作&#xff0c;但是只寫了將近1000條記錄&#xff0c;然后就停止了。在Python版本&#xff1a;2.7.13。在下面的代碼能夠把100萬條記錄全部寫入csv文件而不是.txt文件&…

python中object是什么類型_Python 的 type 和 object 之間是怎么一種關系?

class&#xff0c;metaclass&#xff0c;instance&#xff0c;subclass&#xff0c;base 以下成立&#xff1a; 對任意的A&#xff0c;A是instance&#xff08;推論&#xff1a;任意class也是instance&#xff09; 對任意A&#xff0c;存在B&#xff0c;使得B是A的class A是cla…

java8新生代_jdk8.0的jvm詳情

jstat命令可以查看堆內存各部分的使用量&#xff0c;以及加載類的數量。命令的格式如下&#xff1a;jstat [-命令選項] [vmid] [間隔時間/毫秒] [查詢次數]注意&#xff1a;使用的jdk版本是jdk8.[work16-11-118qf-pms]$ jstat -class 32417Loaded?? Bytes?? Unloaded?? B…

Java中的Runnable、Callable、Future、FutureTask的區別與示例

原文地址&#xff1a;http://blog.csdn.net/bboyfeiyu/article/details/24851847 --------------------------------------------------------- Java中存在Runnable、Callable、Future、FutureTask這幾個與線程相關的類或者接口&#xff0c;在Java中也是比較重要的幾個概念&am…

sql count為空時顯示0_C0010負坐標顯示為正數+紅色0值參考線

小伙伴們早上好啊&#xff01;今天繼續為大家分享柱形圖的美化技巧。希望大家認真閱讀Excel文件和教程&#xff0c;有的圖表看起來簡單&#xff0c;實際上在細節處理上用了很多技巧&#xff0c;大家要多多體會。C0010-負坐標顯示為正數紅色0值參考線效果圖圖表概述本圖可以用來…