歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Java 線程的終止與線程中斷

關於線程終止:

 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 + "]";
    }
}

執行結果:

錯誤資源: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執行權
            }
        }
    }

二、線程的中斷

在上面我們發現使用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();//調用線程中斷方法
    }
}

運行該代碼發現該線程並沒有終止。

2、如何終止線程


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();//調用線程中斷方法
    }
}

看代碼可以發現這與我們自行控制線程的終斷類似。

3、當interrupt() 遇到 sleep() / join ()/wait()時 ,在這裡以sleep() 為例子

 public static native void sleep(long millis) throws InterruptedException;

看源碼可知sleep() 方法  InterruptedException 中斷異常,該異常不是運行時異常,所以需要捕獲它,當線程在執行sleep()時,如果發生線程中斷,這個異常就會產生。該異常一旦拋出就會清除中斷狀態。

看代碼:


public class InterruptTest {
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(){
            @Override
            public void run() {
                while(true){
                    System.out.println("線程狀態"+this.isInterrupted());
                    if(Thread.currentThread().isInterrupted()){//判斷當前線程是否是中斷狀態
                        System.out.println("========true======");
                        break;
                    }
                    try {
                        Thread.sleep(1000);
                        System.out.println("===========sleep()結束===========");
                    } catch (InterruptedException e) {
                        System.out.println("異常:"+e.getMessage());
                      // Thread.currentThread().interrupt();
                    }
                }
            }
        };
        thread.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("=========interrupt()=============");
        thread.interrupt();//調用線程中斷方法
    }
}

執行結果:


線程狀態false
=========interrupt()=============
異常:sleep interrupted
線程狀態false
===========sleep()結束===========
線程狀態false
===========sleep()結束===========
線程狀態false
===========sleep()結束===========

由於線程中斷的狀態被 InterruptedException  異常清除了,所以if()條件中的狀態一直是false ,因此該線程不會被終止。如果去掉注釋就可以達到線程終止的目的(再次中斷自己,設置中斷狀態)。

Copyright © Linux教程網 All Rights Reserved