java多線程系列基礎篇09-----interrupt()和線程終止方式

1 interrup關於interrupt javajdk文檔描述如下

interrupt()的作用是中斷本線程

本線程中斷自己是被允許的;其他線程調用本線程的interrupt()方法時,會通過checkAccess()檢察權限,這有可能跑出SecurityException異常

如果本線程是處於阻塞狀態:調用線程的wait() wait(long,int) 會讓它進入等待(阻塞)狀態,或者調用線程的join() sleep()也會讓他進入阻塞狀態,若線程在阻塞狀態時,調用了它的interrupt()方法,那麼它的"中斷狀態"會被清除,並且會收到一個InterruptedException異常。例如,線程通過wait()進入阻塞狀態。例如,線程通過wait()進入阻塞狀態,此時通過interrupt()中斷線程,調用interrupt()會立即將線程的中斷標記設爲"true",但是由於線程處於阻塞狀態,所以該"中斷標記"會立即被清除爲"false",同時會產生一個InterruptedException的異常,如果線程被阻塞在一個Selector選擇器中,那麼通過interrupt()中斷它時,線程的中斷標記會被設置爲true,並且他會立即從選擇操作中返回

如果不屬於前面所說的情況,那麼通過interrupt()中斷線程時,它的中斷標記會被設置爲"tru

中斷一個"已終止的線程"不會產生任何操作

2 終止線程的方式

Thread中的stop()和suspend()方法,由於固有的不安全性,已經不建議使用

下面,我先分別討論線程在"阻塞狀態","運行狀態"的終止方式,然後再總結出一個通用的方式

2.1 終止處於阻塞狀態的線程

通常,我們通過"中斷"方式終止處於"阻塞狀態"的線程

當線程由於被調用了sleep(),wait(),join()等方法而進入阻塞狀態;若此時調用線程的interrupt()線程中的中斷標記設爲true 由於處於阻塞狀態,中斷標記會被清除,同時產生一個interruptedException異常,將interruptedException放在適當的爲止就能終止線程,形式如下。

@Override
public void run() {
    try {
        while (true) {
            // 執行任務...
        }
    } catch (InterruptedException ie) {  
        // 由於產生InterruptedException異常,退出while(true)循環,線程終止!
    }
}

說明:在while(true)中不斷的執行任務,當線程處於阻塞狀態時,調用線程的interrupt()產生InterruptedException中斷。中斷的捕獲在while(true)之外,這樣就退出了while(true)循環!
注意:對InterruptedException的捕獲務一般放在while(true)循環體的外面,這樣,在產生異常時就退出了while(true)循環。否則,InterruptedException在while(true)循環體之內,就需要額外的添加退出處理。形式如下:

@Override
public void run() {
    while (true) {
        try {
            // 執行任務...
        } catch (InterruptedException ie) {  
            // InterruptedException在while(true)循環體內。
            // 當線程產生了InterruptedException異常時,while(true)仍能繼續運行!需要手動退出
            break;
        }
    }
}

說明:上面的InterruptedException異常的捕獲在whle(true)之內。當產生InterruptedException異常時,被catch處理之外,仍然在while(true)循環體內;要退出while(true)循環體,需要額外的執行退出while(true)的操作。

2.2終止chuyu"運行狀態"的線程。其中包括"中斷標記" "額外添加標記"

01 通過"中斷標記"終止線程

@Override
public void run() {
    while (!isInterrupted()) {
        // 執行任務...
    }
}

說明:isInterrupted()是判斷線程的中斷標記是不是爲true。當線程處於運行狀態,並且我們需要終止它時,可以調用線程的interrupt()方法,使用線程的中斷標記爲true,即isInterrupted()會返回true。此時,就會退出while循環

注意 每個線程都會有一箇中斷標記,在run方法中加以判斷,interrupt()並不會處於"運行狀態"的線程 它會將線程的中斷標記設爲true

02 通過"額外添加標記"

private volatile boolean flag= true;
protected void stopTask() {
    flag = false;
}

@Override
public void run() {
    while (flag) {
        // 執行任務...
    }
}

線程中有一個flag標記,它的默認值是true;並且我們提供stopTask()來設置flag標記,當我們需要終止該線程時,調用該線程的stopTask()方法就可以讓線程退出while循環,將flag定義爲volatile類型,是爲了保證flag的可見性.ji其他線程通過,stopTask()修改了flag之後,本線程就能剛看到修改後的flag的值。

 綜合線程處於"阻塞狀態"和"運行狀態"的終止方式,比較通用的終止線程的形式如下

@Override
public void run() {
    try {
        // 1. isInterrupted()保證,只要中斷標記爲true就終止線程。
        while (!isInterrupted()) {
            // 執行任務...
        }
    } catch (InterruptedException ie) {  
        // 2. InterruptedException異常保證,當InterruptedException異常產生時,線程被終止。
    }
}

3 終止線程的示例

package com.tuhu.filt.javadatasleep;

public class Demo1 {
    public static void main(String[] args) {
        try{
            Thread t1 = new MyThread("t1");
            System.out.println(
                    t1.getName()
                    +" ("
                    +t1.getState()
                    +") is new."
            );
            t1.start();
            System.out.println(t1.getName() +" ("+t1.getState()+") is started.");
            Thread.sleep(300);
            t1.interrupt();
            System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted.");
            Thread.sleep(300);
            System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted now.");


        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
class MyThread extends Thread{
    public MyThread(String name){
        super(name);
    }
    public void run(){
        try{
            int i = 0;
            while(! isInterrupted()){
                Thread.sleep(100);//休眠100ms
                i++;
                System.out.println(
                        Thread.currentThread().getName()
                        +" ("
                        +this.getState()+") loop"
                        + i
                );
            }

        }catch (InterruptedException e){
            System.out.println(
                   Thread.currentThread().getName()
                   + " ("+this.getState()+") catch InterruptedException."
                    );

        }
    }
}
t1 (NEW) is new.
t1 (RUNNABLE) is started.
t1 (RUNNABLE) loop1
t1 (RUNNABLE) loop2
t1 (TIMED_WAITING) is interrupted.
t1 (RUNNABLE) catch InterruptedException.
t1 (TERMINATED) is interrupted now.

01 主線程main中通過new MyThread("t1")創建線程t1,之後t1.start()啓動線程t1

02 t1啓動之後,會不斷檢查它的中斷標記,如果中斷標記爲"false";則休眠100ms

03 t1休眠之後,會切換到主線程main;主線程再次運行,會執行t1.interrupt()中斷線程t1,t1收到中斷指令之後,會將t1的中斷標記設置爲"false",而且會拋出異常我們將捕獲異常的代碼塊移到while循環體內

// Demo2.java的源碼
class MyThread extends Thread {
    
    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        int i=0;
        while (!isInterrupted()) {
            try {
                Thread.sleep(100); // 休眠100ms
            } catch (InterruptedException ie) {  
                System.out.println(Thread.currentThread().getName() +" ("+this.getState()+") catch InterruptedException.");  
            }
            i++;
            System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i);  
        }
    }
}

public class Demo2 {

    public static void main(String[] args) {  
        try {  
            Thread t1 = new MyThread("t1");  // 新建“線程t1”
            System.out.println(t1.getName() +" ("+t1.getState()+") is new.");  

            t1.start();                      // 啓動“線程t1”
            System.out.println(t1.getName() +" ("+t1.getState()+") is started.");  

            // 主線程休眠300ms,然後主線程給t1發“中斷”指令。
            Thread.sleep(300);
            t1.interrupt();
            System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted.");

            // 主線程休眠300ms,然後查看t1的狀態。
            Thread.sleep(300);
            System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted now.");
        } catch (InterruptedException e) {  
            e.printStackTrace();
        }
    } 
}
t1 (NEW) is new.
t1 (RUNNABLE) is started.
t1 (RUNNABLE) loop 1
t1 (RUNNABLE) loop 2
t1 (TIMED_WAITING) is interrupted.
t1 (RUNNABLE) catch InterruptedException.
t1 (RUNNABLE) loop 3
t1 (RUNNABLE) loop 4
t1 (RUNNABLE) loop 5
t1 (TIMED_WAITING) is interrupted now.
t1 (RUNNABLE) loop 6
t1 (RUNNABLE) loop 7
t1 (RUNNABLE) loop 8
t1 (RUNNABLE) loop 9
...

很顯然程序進入了死循環

下面通過額外加標記終止狀態線程的示例

// Demo3.java的源碼
class MyThread extends Thread {

    private volatile boolean flag= true;
    public void stopTask() {
        flag = false;
    }
    
    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        synchronized(this) {
            try {
                int i=0;
                while (flag) {
                    Thread.sleep(100); // 休眠100ms
                    i++;
                    System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i);  
                }
            } catch (InterruptedException ie) {  
                System.out.println(Thread.currentThread().getName() +" ("+this.getState()+") catch InterruptedException.");  
            }
        }  
    }
}

public class Demo3 {

    public static void main(String[] args) {  
        try {  
            MyThread t1 = new MyThread("t1");  // 新建“線程t1”
            System.out.println(t1.getName() +" ("+t1.getState()+") is new.");  

            t1.start();                      // 啓動“線程t1”
            System.out.println(t1.getName() +" ("+t1.getState()+") is started.");  

            // 主線程休眠300ms,然後主線程給t1發“中斷”指令。
            Thread.sleep(300);
            t1.stopTask();
            System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted.");

            // 主線程休眠300ms,然後查看t1的狀態。
            Thread.sleep(300);
            System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted now.");
        } catch (InterruptedException e) {  
            e.printStackTrace();
        }
    } 
}
t1 (NEW) is new.
t1 (RUNNABLE) is started.
t1 (RUNNABLE) loop 1
t1 (RUNNABLE) loop 2
t1 (TIMED_WAITING) is interrupted.
t1 (RUNNABLE) loop 3
t1 (TERMINATED) is interrupted now.

4 interrupted() 和 isInterrupted()的區別

interrupted() 和 isInterrupted()都能夠用於檢測對象的中斷標記

區別是 interrupted()除了返回中斷標記之外,他還會中斷標記(即將中斷標記設爲false),

而isInterrupted()僅僅返回中斷標記

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章