Java線程取消的三種方式
更新時間:2024年12月18日 11:45:09 作者:louisgeek
文章介紹了 Java 線程取消的 3 種方式,不推薦使用 stop 方法和 volatile 設標記位停止線程,線程中斷機制是協作式的,一個線程請求中斷,另一線程響應,線程可檢查自身中斷狀態(tài)或捕獲 InterruptedException 來合適處理以響應中斷,確保安全有序停止,避免資源泄露等問題
Java 線程取消的 3 種方式
- Thread 取消線程通常有以下幾種方式:
- 1 使用 stop 方法來強制停止線程,是非常不安全的方式
- 2 使用 volatile 設置標記位停止線程,是一種不完全可靠的方式
- 3 使用線程中斷機制以協作式的方式來停止線程
- Thread#stop 停止線程,強制停止一個正在運行的線程,該方法已廢棄
- Thread#interrupt 中斷線程,請求線程中斷,將線程的中斷狀態(tài)設置為 true
- Thread#isInterrupted 檢查線程的中斷狀態(tài),返回當前線程的中斷狀態(tài),不會改變線程的中斷狀態(tài)
- Thread#interrupted 檢查并清除線程的中斷狀態(tài),返回當前線程的中斷狀態(tài),并將線程的中斷狀態(tài)重置為 false,不過需要注意的是這是一個靜態(tài)方法
強制停止線程
- 不推薦使用 Thread#stop 來粗暴的停止線程,可能會引起以下問題:
- 1 Thread#stop 會立刻停止 run 方法中剩余的工作,包括在 catch 或 finally 等中的邏輯,因此可能會導致一些清理性的工作的得不到完成,比如文件,數據庫等的關閉操作,可能會導致資源泄露的問題
- 2 Thread#stop 會立即釋放該線程所持有的鎖,可能會導致數據得不到同步,出現數據不一致,也可能導致正在更新的數據結構被損壞等問題
設置 volatile 標記位來停止線程
- 使用 volatile 關鍵字來設置標記位來停止線程在某些情況下是不完全可靠的:
- 1 單純使用 volatile 關鍵字雖然可以保證變量的可見性(即一個線程對變量的修改能夠及時讓其他線程看到),但不能保證復合操作的原子性,可能會導致線程在不正確的時間點結束,導致出現不可預期的行為
- 2 如果線程當前正在阻塞或等待情況下,那么它將不會立刻響應標記位的變化,而是必須等到它從阻塞或等待狀態(tài)恢復過來才有機會去檢查標記位,然后才能開始處理結束流程,時間點可能就出現一定的滯后了
協作式中斷線程
- 線程中斷機制是一種協作式機制,允許一個線程請求另一個線程應該停止其正在執(zhí)行的操作,調用線程 Thread#interrupt 中斷操作并不會直接中斷線程(不會立即停止線程),而只是改變了目標線程的中斷狀態(tài)這個標志位,被請求中斷的線程可以在適當位置(如循環(huán)條件、方法調用前后等)通過檢查自身的中斷狀態(tài)來判斷是否被中斷了,從而采取相應的行動去響應這個中斷請求,不過甚至也可以選擇忽略它(因為響應中斷并不是強制性的要求,這取決于具體的實現和需求)
- 通常情況下線程檢查自身發(fā)現中斷狀態(tài)為 true 時,表示該線程已經被中斷,此時要盡快處理中斷請求,可以選擇去執(zhí)行一些清理資源、保存數據等操作,然后再結束線程
- 這種設計使得線程中斷變得相對安全,因為線程仍有機會在合適的時地方用合理的方式結束自身的工作,而不是像 Thread#stop 那么被強制停止
線程任務執(zhí)行完后正常結束
public static void main(String[] args) { Thread thread = new Thread() { @Override public void run() { //模擬線程執(zhí)行任務 for (int i = 0; i < 100000; i++) { System.out.println("線程運行中 i=" + i); } //在執(zhí)行完所有任務后線程正常結束 System.out.println("---- 線程結束 ----"); } }; thread.start(); }
加入請求線程中斷的邏輯
public static void main(String[] args) { Thread thread = new Thread() { @Override public void run() { //模擬線程執(zhí)行任務 for (int i = 0; i < 100000; i++) { System.out.println("線程運行中 i=" + i); } //雖然調用了 Thread#interrupt 方法請求線程中斷但還是會執(zhí)行完所有的任務 System.out.println("---- 線程結束 ----"); } }; thread.start(); //演示邏輯 try { //等待一段時間后去中斷 thread 線程 Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } System.out.println("請求線程中斷"); //對 thread 線程請求線程中斷 thread.interrupt(); }
說明僅僅只改變中斷狀態(tài)是達不到取消線程的效果,需要繼續(xù)加入響應中斷的邏輯
public static void main(String[] args) { Thread thread = new Thread() { @Override public void run() { //模擬線程執(zhí)行任務 for (int i = 0; i < 100000; i++) { //目的就是讓線程在適當的時候檢查自身的中斷狀態(tài),并完成響應中斷請求的邏輯 if (Thread.currentThread().isInterrupted()) { System.out.println("線程檢測到自身的中斷狀態(tài)為 true ,于是準備停止"); //釋放資源并結束線程 break; //這里用 return 也行 } System.out.println("線程運行中 i=" + i); } // System.out.println("---- 線程結束 ----"); } }; thread.start(); //演示邏輯 try { //等待一段時間后去中斷 thread 線程 Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } System.out.println("請求線程中斷"); //對 thread 線程請求線程中斷 thread.interrupt(); }
存在阻塞狀態(tài)(比如 Object#wait、Thread#sleep、Thread#join 或 Condition#await 等方法調用產生的)下的響應中斷
public static void main(String[] args) { Thread thread = new Thread() { @Override public void run() { //模擬線程執(zhí)行任務 for (int i = 0; i < 100000; i++) { try { Thread.sleep(2); } catch (InterruptedException e) { //當一個線程在阻塞狀態(tài)時如果調用了該線程的 interrupt 方法的話,那么阻塞方法就會拋出 InterruptedException 異常,類似于線程檢測到自身的中斷狀態(tài)為 true,也就意味著這里需要加入響應中斷的邏輯了 //這里拋出 InterruptedException 異常的設計是為了線程可以從阻塞狀態(tài)恢復(喚醒)過來(表示阻塞操作由于中斷而提前結束),能在線程結束前有機會去處理中斷請求 //另外拋出 InterruptedException 異常的同時會清除線程的中斷標志位(中斷狀態(tài)被重置為 false) //所以這里可以做一些停止線程任務繼續(xù)執(zhí)行的邏輯(比如直接退出循環(huán))或者也可以在這里再次調用 Thread#interrupt 重設中斷狀態(tài)(標記回中斷狀態(tài)為 true)然后和適當位置的 Thread#isInterrupted() 判斷配合來完成響應中斷請求的邏輯 break; } System.out.println("線程運行中 i=" + i); } // System.out.println("---- 線程結束 ----"); } }; thread.start(); //演示邏輯 try { //等待一段時間后去中斷 thread 線程 Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } System.out.println("請求線程中斷"); //對 thread 線程請求線程中斷 thread.interrupt(); }
線程在阻塞狀態(tài)下也能正確響應中斷請求
public static void main(String[] args) { Thread thread = new Thread() { @Override public void run() { //模擬線程執(zhí)行任務 for (int i = 0; i < 100000; i++) { //適當位置檢查自身的中斷狀態(tài),并完成響應中斷請求的邏輯 if (Thread.currentThread().isInterrupted()) { System.out.println("線程檢測到自身的中斷狀態(tài)為 true ,于是準備停止"); //釋放資源并結束線程 break; //這里用 return 也行 } try { Thread.sleep(2); } catch (InterruptedException e) { //拋出 InterruptedException 異常的同時會清除中斷標志位(中斷狀態(tài)被重置為 false) //所以這里要特別注意,可以按需重設中斷標志位,就能做到即使線程在阻塞狀態(tài)下也能夠正確地響應中斷請求了(不然很容易錯過外部設置的那一次中斷請求) Thread.currentThread().interrupt(); } System.out.println("線程運行中 i=" + i); } // System.out.println("---- 線程結束 ----"); } }; thread.start(); //演示邏輯 try { //等待一段時間后去中斷 thread 線程 Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } System.out.println("請求線程中斷"); //對 thread 線程請求線程中斷 thread.interrupt(); }
總結
- 不推薦使用 stop 方法來強制停止線程,也不推薦使用 volatile 設置標記位停止線程
- 線程中斷機制是一種交互式取消方式,一個線程請求另一個線程中斷,而另一個線程就是對這個線程的請求中斷做出響應(一個線程不應該由其他線程來直接強制中斷或者停止,而是應該由線程自己自行進行停止,因為任務本身的代碼肯定比發(fā)出取消請求的代碼更清楚了解自身該如何執(zhí)行清除釋放結束等工作,說到底目標線程比調用者更加了解自身線程應不應該被停止,何時停止,如何停止)
- 線程自己檢查自身中斷狀態(tài)或者捕獲 InterruptedException 然后進行合適的處理以響應中斷
- 通過合理地運用線程中斷機制,可以確保線程能夠安全、有序地停止,從而避免資源泄露以及一些潛在的問題
以上就是Java線程取消的三種方式的詳細內容,更多關于Java線程取消的資料請關注腳本之家其它相關文章!
相關文章
大廠禁止SpringBoot在項目使用Tomcat容器原理解析
這篇文章主要為大家介紹了大廠禁止SpringBoot在項目使用Tomcat原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-07-07修改Maven settings.xml 后配置未生效的解決
這篇文章主要介紹了修改Maven settings.xml 后配置未生效的解決,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10IntelliJ IDEA中出現"PSI and index do not match"錯誤的解決辦法
今天小編就為大家分享一篇關于IntelliJ IDEA中出現"PSI and index do not match"錯誤的解決辦法,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-10-10java.lang.ExceptionInInitializerError初始化程序中的異常錯誤的解決
java.lang.ExceptionInInitializerError?異常在?Java?中表示一個錯誤,該錯誤發(fā)生在嘗試初始化一個類的靜態(tài)變量、靜態(tài)代碼塊或枚舉常量時,本文就來介紹并解決一下,感興趣的可以了解一下2024-05-05