線程的終止stop與線程的中斷interrupted
阿新 • • 發佈:2017-09-03
需要 run sed ktr alt 技術分享 進程 lee blog
線程除了運行完畢,自動進入死亡狀態,也可以手動進行停止,Thread類也提供了2個類方法來進行線程的停止。
一、stop
如圖所示,stop方法已經被標記為過時的,不推薦的。因為這這個方法太過於暴力,會立即殺死進程,導致數據不能同步,帶來很難排查的錯誤。
下面是一段造成錯誤信息的代碼:
1 public class StopThreadUnsafe { 2 public static User u = new User(); 3 4 public static class User { 5 private int id; 6 private錯誤代碼String name; 7 8 9 public User() { 10 this.id = 0; 11 this.name = "0"; 12 } 13 14 public int getId() { 15 return id; 16 } 17 18 public void setId(int id) { 19 this.id = id; 20 } 21 22 public String getName() {23 return name; 24 } 25 26 public void setName(String name) { 27 this.name = name; 28 } 29 30 @Override 31 public String toString() { 32 return "User{" + 33 "id=" + id + 34 ", name=‘" + name + ‘\‘‘ + 35‘}‘; 36 } 37 } 38 39 public static class ChangeObjectThread extends Thread { 40 volatile boolean stopme = false; 41 42 public void stopMe() { 43 stopme = true; 44 } 45 46 @Override 47 public void run() { 48 while (true) { 49 // if (stopme) { 50 // System.out.println("exit by stop me "); 51 // break; 52 // } 53 synchronized (u) { 54 int v = (int) (System.currentTimeMillis() / 1000); 55 u.setId(v); 56 //暫停一段時間 57 58 try { 59 Thread.sleep(100); 60 } catch (InterruptedException e) { 61 e.printStackTrace(); 62 } 63 //暫停之後再寫入值 64 u.setName(String.valueOf(v)); 65 } 66 Thread.yield(); 67 } 68 } 69 } 70 71 public static class ReadObjectThread extends Thread { 72 @Override 73 public void run() { 74 while (true) { 75 synchronized (u) { 76 if (u.getId() != Integer.parseInt(u.getName())) { 77 System.out.println(u.toString()); 78 } 79 } 80 Thread.yield(); 81 } 82 } 83 } 84 85 86 public static void main(String[] args) throws InterruptedException { 87 new ReadObjectThread().start(); 88 while (true) { 89 Thread t = new ChangeObjectThread(); 90 t.start(); 91 Thread.sleep(150); 92 t.stop(); 93 } 94 } 95 }
運行起來的結果是:
裏面情況是id和name是同一個值,但是被強行stop掉了線程,導致數據混亂。
那麽如何停止一個線程呢?
在線程體中加一個flag,每次執行線程體時查看標桿,如果標桿有效,則自動退出,如代碼中,就有一個stopme方法,在合適的時候調用就可以停止線程,而且是緩和的,基本上不會帶來數據丟失的問題。
二、interrupt
stop既然這麽坑,所有JDK還是給了解決辦法的,那就是線程中斷Interrupt。
JDK裏面有4個關於interrupt的方法,
public void interrupt()
中斷線程,該方法是一個實例方法,它通知目標線程中斷,也就是設置中斷標誌位。,中斷標誌位標識當前線程已經被中斷了。tips:給線程加了中斷,並不會對線程起實質性作用,僅僅是設置了中斷標誌位,還需要進行一系列退出操作才可以進行線程的終止。public boolean isInterrupted()判斷當前線程是否有被中斷,通過檢查中斷標誌位來判斷。public static boolean interrupted()
interrupted也是用來判斷當前線程的中斷狀態,但同時會清除當前能線程的中斷標誌位狀態。private native boolean isInterrupted(boolean ClearInterrupted)
代碼示例:
1 /** 2 * 關於中斷Interrupt 3 * Created by huxingyue on 2017/9/3. 4 */ 5 public class InterruptAbout { 6 public static void main(String[] args) throws InterruptedException { 7 Thread t1 = new Thread() { 8 @Override 9 public void run() { 10 while (true) { 11 //這才是合理的退出 12 if (Thread.currentThread().isInterrupted()) { 13 System.out.println("here is interrupted "); 14 break; 15 } 16 System.out.println("這裏執行了一次yield"); 17 18 System.out.println("1當前線程是中斷狀態嗎?"+Thread.currentThread().isInterrupted()); 19 try { 20 System.out.println("此處準備睡眠"); 21 Thread.sleep(2000); 22 } catch (InterruptedException e) { 23 System.out.println("interrupted when sleep"); 24 //應該在這裏再次加中斷,sleep報出異常後會清除中斷標誌 25 System.out.println("2當前線程是中斷狀態嗎?"+Thread.currentThread().isInterrupted()); 26 Thread.currentThread().interrupt(); 27 } 28 29 30 Thread.yield(); 31 } 32 } 33 }; 34 t1.start(); 35 Thread.sleep(2000); 36 t1.interrupt(); 37 } 38 }View Code
while裏面加了一個if去判斷線程的中斷標誌位,如果有中斷標誌的話,就可以合理退出。
值的註意的是,當調用sleep()時,可能被interrupt()方法打斷,這這時就會拋出InterruptedException,不僅僅是會拋出異常,而且還會清除標誌位,所以在catch語句中會再次添加中斷標誌。
線程的終止stop與線程的中斷interrupted