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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

如何暂停一个正在运行的线程?

發布時間:2023/12/10 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何暂停一个正在运行的线程? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

今天把小伙伴問懵了,小剛,你知道怎么停止一個線程嗎?

這…,這…,stop?

原來平時小剛這小子只知道創建線程,不知道怎么暫停線程呀~[狗頭]


停止線程是在多線程開發中很重要的技術點,比如在多線程持續處理業務代碼時,由于處理邏輯中有第三方接口異常,我們就假設發送短信接口掛了吧,那么此時多線程調用短信接口是沒有任何意義的,我們希望接口恢復后再對接口進行處理,那么此時怎么辦呢,如何中止已經啟動的線程呢?

其實在Java中有3種方式可以終止正在運行的線程:

  • 使用stop方法強制退出:使用stop()方法強制終止線程,注意,強烈不推薦這種方式,并且該方法已經被標記為過期方法了。
  • 使用interrupt方法中斷線程,該方法只是告訴線程要終止,但最終何時終止取決于計算機;
  • 設置標志位:使用設置退出標志,使線程正常退出,也就是當run方法完成后線程終止;
  • 盡管羅列了三種方式,但由于存在安全問題,所以舍棄了stop()方法,怎么就安全問題了呢?

    暴力停止線程的stop()方法「禁止使用」

    之所以說stop()方法暴力是相對于其他兩種方式的,只要調用stop()方法,運行中的線程就暫停了,我們通過一段代碼測試一下:

    public class MyTest {public static void main(String[] args) {try {/**創建線程**/ThreadDemo demo = new ThreadDemo();/**開啟線程**/demo.start();/**線程休眠**/Thread.sleep(5000);/**停止線程**/demo.stop();} catch (InterruptedException e) {e.printStackTrace();}}}public class ThreadDemo extends Thread{/**變量i**/private int i = 0;@Overridepublic void run() {try {while (true){i++;System.out.println("輸出i:"+i);Thread.sleep(1000);}}catch (InterruptedException e){System.out.println("拋出異常");}} }

    執行結果如下:

    輸出i:1 輸出i:2 輸出i:3 輸出i:4 輸出i:5

    如上,我們創建了一個死循環輸出的線程ThreadDemo,每隔一秒輸出i++,但是當遇到stop()方法后,就不再輸出了,不對,看上去沒問題呀,stop() 方法這不用的好好的嗎?

    嗨,怪就怪這個例子太簡單了吧,我們來看看弄點帶操作對象的例子,首先創建一個用戶實體:

    public class UserModel {/*** 給定userName+password默認值* 用于模擬上一個線程給賦的舊值*/private String userName = "張三";private String password = "hahahha";/*** 用于復制的方法* 為防止多線程數據錯亂,加上synchronized關鍵字* @param userName* @param password*/synchronized public void setValue(String userName, String password){try {this.userName = userName;Thread.sleep(3000);this.password = password;} catch (InterruptedException e) {e.printStackTrace();}}省略get\set方法... }

    然后我們再在ThreadDemo中使用這個實體:

    public class ThreadDemo extends Thread{private UserModel userModel;public ThreadDemo(UserModel userModel){this.userModel = userModel;}@Overridepublic void run() {/*** 重新設置用戶名+密碼* 用戶名:niceyoo* 密碼:123456*/userModel.setValue("niceyoo","123456");} }

    然后在MyTest中創建并啟動線程,然后調用stop()方法:

    public class MyTest {public static void main(String[] args) {try {/**創建用戶實體**/UserModel userModel = new UserModel();/**創建線程**/ThreadDemo demo = new ThreadDemo(userModel);/**開啟線程**/demo.start();/**線程休眠**/Thread.sleep(1000);/**停止線程**/demo.stop();/**輸出用戶實體**/System.out.println(userModel.getUserName() + " :" + userModel.getPassword());} catch (ThreadDeath | InterruptedException e) {e.printStackTrace();}}}

    輸出結果如下:

    niceyoo :hahahha

    顯然跟我們預期的輸出結果niceyoo\123456不一致,使用stop()釋放鎖,對鎖定的對象進行了解鎖,導致數據得不到同步的處理,出現數據不一致的情況,所以這樣就會導致數據安全問題,這也是現在為何 stop() 方法被標注為 “作廢、過期”。

    interrupted()方法「只告訴要停止,不知道何時停」

    使用interrupted()方法就不像是stop()方法那樣簡單粗暴了,調用該方法僅僅是在當前線程中打了一個停止的標記,并不是真的停止線程,就好比,我打電話告訴你不要玩游戲了,但是你什么時候停止玩游戲就是你的事了。

    public class MyTest {public static void main(String[] args) {try {/**創建線程**/ThreadDemo2 demo = new ThreadDemo2();/**開啟線程**/demo.start();/**線程休眠**/Thread.sleep(2000);/**停止線程**/demo.interrupt();} catch (InterruptedException e) {System.out.println("線程已經暫停");e.printStackTrace();}}}public class ThreadDemo2 extends Thread{@Overridepublic void run() {try {for (int i = 0; i < 1800000; i++) {if(!this.isInterrupted()){System.out.println("輸出i:"+i++ + " - 線程未停止 ");}else{System.out.println("輸出i:"+i++ + " - 線程已停止 - 拋出異常");throw new InterruptedException();}}}catch (InterruptedException e){System.out.println("線程已結束...");}} }

    輸出結果:

    輸出i:1499992 - 線程未停止 ... 輸出i:1700624 - 線程未停止 輸出i:1700626 - 線程未停止 輸出i:1700628 - 線程已停止 - 拋出異常 線程已結束...

    簡單說一下上方代碼,首先我們創建了一個for循環輸出i++的線程,啟動線程后調用 interrupt() 方法停止線程,但是啥時候停止是不可控的,雖然不可控但是還是有方法知道線程是否是停止的,我們在ThreadDemo2線程類中看到 if 判斷 — this.isInterrupted() 「等價于Thread.currentThread().isInterrupt() 」,這是用來判斷當前線程是否被終止,通過這個判斷我們可以做一些業務邏輯處理,通常如果this.isInterrupted被判定為true后,我們會拋一個中斷異常,然后通過try-catch捕獲。

    再額外說一下,有的小伙伴設置的 for 循環變量的最大值比較小,測試執行過程中并沒有重現線程被終止,然后就懷疑這個 interrupt() 到底能不能停止線程呀, 不用糾結,這正是線程的自主權,我們無法像 stop() 方法一樣直接停止線程的。

    設置標志位

    設置標志位是用到了共享變量的方式,我們了解線程對于變量的操作都是操作的變量副本,而一旦使用

    volatile關鍵字修飾后,因為其可見性,變量變更始將終從主存中獲取最新值。

    public class MyTest {public static void main(String[] args) {/**創建2個線程**/ThreadDemo3 demo1 = new ThreadDemo3();ThreadDemo3 demo2 = new ThreadDemo3();demo1.setName("線程1");demo2.setName("線程2");/**開啟線程**/demo1.start();demo2.start();/**讓線程先運行5s**/try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}/**修改線程的變量**/demo1.heartbeat = false;demo2.heartbeat = false;System.out.println("----暫停線程----");/**讓線程再運行5s**/try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}/**再將標志為置為true**/demo1.heartbeat = true;demo2.heartbeat = true;System.out.println("----從新開啟線程----");} }public class ThreadDemo3 extends Thread{/**共享變量**/volatile Boolean heartbeat = true;@Overridepublic void run() {while (true){/**判斷標志是否為true**/if (heartbeat){System.out.println("當前運行線程為:" +Thread.currentThread().getName() + " - 運行");}else{System.out.println("當前運行線程為:" +Thread.currentThread().getName() + " - 非運行");}try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}} }

    輸出結果:

    省略ing... 當前運行線程為:線程1 - 運行 當前運行線程為:線程2 - 運行 ----暫停線程---- 省略ing... 當前運行線程為:線程1 - 非運行 當前運行線程為:線程2 - 非運行 ----從新開啟線程---- 當前運行線程為:線程1 - 運行 當前運行線程為:線程2 - 運行 省略ing...

    來看一下上方代碼,我們在線程類里創建了共享變量heartbeat,因為要監聽這個貢獻變量的狀態,肯定是要用while循環體了,為了演示狀態的變更,所以在while循環體代碼中沒有throw拋出 InterruptedException 異常,正常情況下在判斷共享變量為fasle時,也是要手動拋出異常的,ok,這就是設置標志位了。

    總結一定要看的

    stop()方法在這就不提了,肯定是行不通的,至于為何不能使用大家可以再仔細看看上方那個例子。

    然后是interrupt()方法+拋異常處理,看完上邊那個例子,大家可能會覺得這個方法有點問題,暫停線程完全靠線程自身決定,即便調用了也不能快速的停止線程,但是我要告訴你,這是目前最為正確的方式… 咳咳,別著急,咱先把設置標志位說了。

    設置標志位使用了volatile關鍵字共享變量方式,通過改變共享變量+拋異常的方式來暫停線程,這個看起來最有效,最正確的方式,其實有一點點問題,而這一點點問題就是為什么讓 interrupt() 成為最正確的方式。

    volatile標記共享變量方式,在線程發生阻塞時是無法完成響應的。

    這個所謂的阻塞指的是什么呢?

    其實發生阻塞的情況是比較常見的,比如調用 Thread.join() 方法「當前線程陷入無限期的阻塞,join() 所屬的線程對象正常運行run()方法,對join()方法不了解的小伙伴可以去百度了」,或者是 Thread.sleep() 方法,再或者是線程需要等待鍵盤輸入而被阻塞,還有socket網絡編程中的 ServerSocket.accept() 方法等等等,總之,在這些種種情況下讓線程處于不可運行狀態時,即便是主線程修改了共享變量的值,該線程此時根本無法檢查循環標志,所以也就無法實現線程中斷。

    所以,interrupt() + 手動拋異常的方式是目前中斷一個正在運行的線程最為正確的方式了。

    如果覺得這篇文章對你有用,可以左上角關注一下我呀~

    關注我的公眾號吧,與一萬+小伙伴一起成長~

    總結

    以上是生活随笔為你收集整理的如何暂停一个正在运行的线程?的全部內容,希望文章能夠幫你解決所遇到的問題。

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