關於線程終止:
1、一般來講線程在執行完畢後就會進入死亡狀態,那該線程自然就終止了。
2、一些服務端的程序,可能在業務上需要,常駐系統。它本身是一個無窮的循環,用於提供服務。那對於這種線程我們該如何結束它呢。
在Thread類中JDK給我們提供了一個終止線程的方法stop(); 該方法一經調用就會立即終止該線程,並立即釋放對象鎖。如果當一個線程執行一半業務而調用了該方法,可能就會產生數據不一致問題。
數據一致性:同一時間點,你在節點A中獲取到key1的值與在節點B中獲取到key1的值應該都是一樣的。
例如:數據庫中維護一張用戶 student 表 ,表裡有兩條數據 :
id=1 name="大A" id=2 name="小a"
如果我們使用一個 Student 對象來保存這些記錄,那麼該對象要麼保存id=1 de 記錄 , 要麼保存id=2的記錄。如果這個Student對象一半保存id=1的記錄 一半保存id=2 的記錄(即 id=1 name="小a"), 那麼數據就出現了數據一致性問題。
看圖來說明stop為什麼會產生數據一致性問題:
讀與寫操作每次都要活的student對象鎖,只有獲得該鎖的線程才有權利操作該對象,也就是說student對象鎖的作用就是為了維護對象的一致性,如果線程在寫入數據寫到一半時 ,調用stop方法,那該對象就會被破壞同時也會釋放該對象鎖,另外一個等待該鎖的讀線程就會獲得鎖,執行操作讀到的數據顯然是錯誤的。
代碼示例:
public class StopTest2 { private static Student student=new Student(); public static void main(String[] args) { new Thread(new Thread_read()).start(); while(true){ Thread thread_writer=new Thread(new Thread_writer()); thread_writer.start(); try { Thread.sleep(1500); } catch (InterruptedException e) { e.printStackTrace(); } thread_writer.stop(); } } static class Thread_read implements Runnable{ @Override public void run() { while(true){ synchronized (student){//對共享資源加鎖,使讀寫分離互不影響 ,維護對象的一致性 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if(student.getId()!=Integer.parseInt(student.getName())){ System.out.println("錯誤資源:"+student); }else{ System.out.println("正確資源:"+student); } } Thread.yield();//釋放cup執行權 } } } static class Thread_writer implements Runnable{ @Override public void run() { while(true){ synchronized (student){//對共享資源加鎖,使讀寫分離互不影響,維護對象的一致性 int mm=new Random().nextInt(10); student.setId(mm); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } student.setName(String.valueOf(mm)); } Thread.yield();//釋放cup執行權 } } } } class Student{ private int id=0; private String name="0"; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Student [id=" + id + ", name=" + name + "]"; } } View Code執行結果:
錯誤資源:Student [id=5, name=8] 錯誤資源:Student [id=4, name=8] 錯誤資源:Student [id=2, name=5]
如何讓正確的終止線程:由程序自行決定線程的終止時間。定義一個標識,通過改變標識來控制程序是否執行。
static class Thread_writer implements Runnable{ private boolean flag=false; public void setFlag(boolean flag){ this.flag=flag; } @Override public void run() { while(!flag){ synchronized (student){//對共享資源加鎖,使讀寫分離互不影響,維護對象的一致性 int mm=new Random().nextInt(10); student.setId(mm); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } student.setName(String.valueOf(mm)); } Thread.yield();//釋放cup執行權 } } } View Code在上面我們發現使用stop終止線程會照成數據一致性問題,於是我們通過控制標識來控制線程的終止,那JDK有沒有合適的終止線程的方式呢?那就就是“線程中斷”
線程中斷就是讓目標線程停止執行,但它不會使線程立即終止,而是給線程發送一個通知,告訴線程jvm希望你退出執行,至於目標線程何時退出,則完全由它自己決定(如果立即停止,會造成與stop一樣的問題)。
JDK中線程中斷相關的三個方法:
//線程中斷 public void interrupt(){} //判斷線程是否中斷 public boolean isInterrupted() {} //判斷線程是否中斷,並清除當前中斷狀態 public static boolean interrupted(){}
1、使用線程中斷就一定會中斷線程嗎?
public class InterruptTest { public static void main(String[] args) { Thread thread=new Thread(){ @Override public void run() { while(true){ System.out.println("========true======"); } } }; thread.start(); try { Thread.sleep(0); } catch (InterruptedException e) { e.printStackTrace(); } thread.interrupt();//調用線程中斷方法 } } View Code運行該代碼發現該線程並沒有終止。
如何終止線程
public class InterruptTest { public static void main(String[] args) { Thread thread=new Thread(){ @Override public void run() { while(true){ if(this.isInterrupted()){//判斷當前線程是否是中斷狀態 System.out.println("========true======"); break; } } } }; thread.start(); try { Thread.sleep(0); } catch (InterruptedException e) { e.printStackTrace(); } thread.interrupt();//調用線程中斷方法 } } View Code看代碼可以發現這與我們自行控制線程的終斷類似。但線程中斷的方式更厲害。