java併發之中止線程

  中止一個線程意味着在任務處理完任務以前停掉正在作的操做,也就是放棄當前的操做。中止一個線程能夠用Thread.stop()方法,但最好不要用它。雖然它確實能夠中止一個正在運行的線程,可是這個方法是不安全的,並且是已被廢棄的方法。
在java中有如下3種方法能夠終止正在運行的線程:html

  1. 使用退出標誌,使線程正常退出,也就是當run方法完成後線程終止。(咱們什麼也不作)
  2. 使用stop方法強行終止,可是不推薦這個方法,由於stop和suspend及resume同樣都是過時做廢的方法。(調用stop)
  3. 使用interrupt方法中斷線程:(1:咱們本身用interrupted()或isInterrupted()方法判斷是否當前線程是否被打了中止標誌(調用interrupt方法打標誌),若是打了本身new異常對象拋出,即中止了線程;2:interruupt方法打中止標誌和sleep結合,先睡再打或者先打再睡都會自動拋出 InterruptedException 異常對象,咱們捕獲便可。

1. 中止不了的線程

interrupt()方法的使用效果並不像for+break語句那樣,立刻就中止循環。調用interrupt方法是在當前線程中打了一箇中止標誌,並非真的中止線程java

 1 public class MyThread extends Thread {
 2     public void run(){
 3         super.run();
 4         for(int i=0; i<500000; i++){
 5             System.out.println("i="+(i+1));
 6         }
 7     }
 8 }
 9 
10 public class Run {
11     public static void main(String args[]){
12         Thread thread = new MyThread();
13         thread.start();
14         try {
15             Thread.sleep(2000);
16             thread.interrupt();
17         } catch (InterruptedException e) {
18             e.printStackTrace();
19         }
20     }
21 }

輸出結果:編程

...
i=499994
i=499995
i=499996
i=499997
i=499998
i=499999
i=500000

從運行結果看調用interrupt方法並無中止線程。安全

2. 判斷線程是否中止狀態

Thread.java類中提供了兩種方法:多線程

  1. this.interrupted(): 測試當前線程是否已經中斷;
  2. this.isInterrupted(): 測試線程是否已經中斷;

那麼這兩個方法有什麼圖區別呢?
咱們先來看看this.interrupted()方法的解釋:測試當前線程是否已經中斷,當前線程是指運行this.interrupted()方法的線程。ide

 1 public class MyThread extends Thread {
 2     public void run(){
 3         super.run();
 4         for(int i=0; i<500000; i++){
 5             i++;
 6 //            System.out.println("i="+(i+1));
 7         }
 8     }
 9 }
10 
11 public class Run {
12     public static void main(String args[]){
13         Thread thread = new MyThread();
14         thread.start();
15         try {
16             Thread.sleep(2000);
17             thread.interrupt();
18 
19             System.out.println("stop 1??" + thread.interrupted());
20             System.out.println("stop 2??" + thread.interrupted());
21         } catch (InterruptedException e) {
22             e.printStackTrace();
23         }
24     }
25 }

運行結果:學習

stop 1??false
stop 2??false

類Run.java中雖然是在thread對象上調用如下代碼:thread.interrupt(), 後面又使用測試

System.out.println("stop 1??" + thread.interrupted());
System.out.println("stop 2??" + thread.interrupted());  

來判斷thread對象所表明的線程是否中止,但從控制檯打印的結果來看,線程並未中止,這也證實了interrupted()方法的解釋,測試當前線程是否已經中斷。這個當前線程是main,它從未中斷過,因此打印的結果是兩個false.this

如何使main線程產生中斷效果呢?spa

public class Run2 {
    public static void main(String args[]){
        Thread.currentThread().interrupt();
        System.out.println("stop 1??" + Thread.interrupted());
        System.out.println("stop 2??" + Thread.interrupted());

        System.out.println("End");
    }
}    

運行效果爲:

stop 1??true
stop 2??false
End

方法interrupted()的確判斷出當前線程是不是中止狀態。但爲何第2個布爾值是false呢? 官方幫助文檔中對interrupted方法的解釋:
測試當前線程是否已經中斷。線程的中斷狀態由該方法清除。 換句話說,若是連續兩次調用該方法,則第二次調用返回false。

下面來看一下isInterrupted()方法。

public class Run3 {
    public static void main(String args[]){
        Thread thread = new MyThread();
        thread.start();
        thread.interrupt();
        System.out.println("stop 1??" + thread.isInterrupted());
        System.out.println("stop 2??" + thread.isInterrupted());
    }
}

運行結果:

stop 1??true
stop 2??true

isInterrupted()並不會清除狀態,因此打印了兩個true。

因此:

this.interrupted():測試當前線程是否已是中斷狀態,執行後具備將狀態標誌位清除爲false的功能。

this.isInterrupted():測試線程Thread對象是否已是中斷狀態,但不清除狀態標誌。

3. 能中止的線程--異常法

有了前面學習過的知識點,就能夠在線程中用for語句來判斷一下線程是不是中止狀態,若是是中止狀態,則後面的代碼再也不運行便可:

public class MyThread extends Thread {
    public void run(){
        super.run();
        for(int i=0; i<500000; i++){
            if(this.interrupted()) {
                System.out.println("線程已經終止, for循環再也不執行");
                break;
            }
            System.out.println("i="+(i+1));
        }
    }
}

public class Run {
    public static void main(String args[]){
        Thread thread = new MyThread();
        thread.start();
        try {
            Thread.sleep(2000);
            thread.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

運行結果:

...
i=202053
i=202054
i=202055
i=202056
線程已經終止, for循環再也不執行

上面的示例雖然中止了線程,但若是for語句下面還有語句,仍是會繼續運行的。看下面的例子:

 1 public class MyThread extends Thread {
 2     public void run(){
 3         super.run();
 4         for(int i=0; i<500000; i++){
 5             if(this.interrupted()) {
 6                 System.out.println("線程已經終止, for循環再也不執行");
 7                 break;
 8             }
 9             System.out.println("i="+(i+1));
10         }
11 
12         System.out.println("這是for循環外面的語句,也會被執行");
13     }
14 }

使用Run.java執行的結果是:

...
i=180136
i=180137
i=180138
i=180139
線程已經終止, for循環再也不執行
這是for循環外面的語句,也會被執行

如何解決語句繼續運行的問題呢? 看一下更新後的代碼:(用了拋異常的方式,當檢測到線程被中斷(即有中斷標誌位時)拋異常

public class MyThread extends Thread {
    public void run(){
        super.run();
        try {
            for(int i=0; i<500000; i++){
                if(this.interrupted()) {
                    System.out.println("線程已經終止, for循環再也不執行");
                        throw new InterruptedException();
                }
                System.out.println("i="+(i+1));
            }

            System.out.println("這是for循環外面的語句,也會被執行");
        } catch (InterruptedException e) {
            System.out.println("進入MyThread.java類中的catch了。。。");
            e.printStackTrace();
        }
    }
}

使用Run.java運行的結果以下:

...
i=203798
i=203799
i=203800
線程已經終止, for循環再也不執行
進入MyThread.java類中的catch了。。。
java.lang.InterruptedException
    at thread.MyThread.run(MyThread.java:13)

4. 在沉睡中中止

若是線程在sleep()狀態下中止線程,會是什麼效果呢?

package stopThread;

public class SleepThread1 extends Thread {
    public void run(){
        super.run();

        try {
            System.out.println(this.currentThread().getId() + "線程開始。。。");
            Thread.sleep(2000);//睡2000毫秒
            System.out.println(this.currentThread().getId() + "線程結束。");
        } catch (InterruptedException e) {
            System.out.println(this.currentThread().getId() +"在沉睡中被中止, 進入catch, 調用isInterrupted()方法的結果是:" + this.isInterrupted());
            e.printStackTrace();
        }

    }
}

 

package stopThread;

public class Run1 {
    public static void main(String args[]){
        Thread thread1 = new SleepThread1();
        Thread thread2 = new SleepThread1();
        
        thread1.start();
        thread2.start();
        
        thread1.interrupt();          
    }
}

運行的結果是:

 

前一個實驗是先sleep而後再用interrupt()中止,與之相反的操做在學習過程當中也要注意:

public class MyThread extends Thread {
    public void run(){
        super.run();
        try {
            System.out.println("線程開始。。。");
            for(int i=0; i<10000; i++){
                System.out.println("i=" + i);
            }
            Thread.sleep(200000);
            System.out.println("線程結束。");
        } catch (InterruptedException e) {
             System.out.println("先中止,再遇到sleep,進入catch異常");
            e.printStackTrace();
        }

    }
}

public class Run {
    public static void main(String args[]){
        Thread thread = new MyThread();
        thread.start();
        thread.interrupt();
    }
}

運行結果:

i=9998
i=9999
先中止,再遇到sleep,進入catch異常
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at thread.MyThread.run(MyThread.java:15)

說明:

不管先sleep,後interrupt,仍是先interrupt,後sleep,後者運行都會拋出 InterruptedException異常(即中止的線程),咱們catch該異常便可。

順便說一下,上面單獨用interrupt是中止不了線程的,是咱們本身new了異常對象拋出(咱們拋以前還判斷了是否有interrupt標誌位)才中止的線程

5. 能中止的線程---暴力中止

使用stop()方法中止線程則是很是暴力的。

 1 public class MyThread extends Thread {
 2     private int i = 0;
 3     public void run(){
 4         super.run();
 5         try {
 6             while (true){
 7                 System.out.println("i=" + i);
 8                 i++;
 9                 Thread.sleep(200);
10             }
11         } catch (InterruptedException e) {
12             e.printStackTrace();
13         }
14     }
15 }
16 
17 public class Run {
18     public static void main(String args[]) throws InterruptedException {
19         Thread thread = new MyThread();
20         thread.start();
21         Thread.sleep(2000);
22         thread.stop();
23     }
24 }

運行結果:

i=0
i=1
i=2
i=3
i=4
i=5
i=6
i=7
i=8
i=9

Process finished with exit code 0
6.方法stop()與java.lang.ThreadDeath異常

調用stop()方法時會拋出java.lang.ThreadDeath異常,可是一般狀況下,此異常不須要顯示地捕捉。

 

public class MyThread extends Thread {
    private int i = 0;
    public void run(){
        super.run();
        try {
            this.stop();
        } catch (ThreadDeath e) {
            System.out.println("進入異常catch");
            e.printStackTrace();
        }
    }
}

public class Run {
    public static void main(String args[]) throws InterruptedException {
        Thread thread = new MyThread();
        thread.start();
    }
}

stop()方法以及做廢,由於若是強制讓線程中止有可能使一些清理性的工做得不到完成。另一個狀況就是對鎖定的對象進行了解鎖,致使數據得不到同步的處理,出現數據不一致的問題。

7. 釋放鎖的不良後果

使用stop()釋放鎖將會給數據形成不一致性的結果。若是出現這樣的狀況,程序處理的數據就有可能遭到破壞,最終致使程序執行的流程錯誤,必定要特別注意:

 1 public class SynchronizedObject {
 2     private String name = "a";
 3     private String password = "aa";
 4 
 5     public synchronized void printString(String name, String password){
 6         try {
 7             this.name = name;
 8             Thread.sleep(100000);
 9             this.password = password;
10         } catch (InterruptedException e) {
11             e.printStackTrace();
12         }
13     }
14 
15     public String getName() {
16         return name;
17     }
18 
19     public void setName(String name) {
20         this.name = name;
21     }
22 
23     public String getPassword() {
24         return password;
25     }
26 
27     public void setPassword(String password) {
28         this.password = password;
29     }
30 }
31 
32 public class MyThread extends Thread {
33     private SynchronizedObject synchronizedObject;
34     public MyThread(SynchronizedObject synchronizedObject){
35         this.synchronizedObject = synchronizedObject;
36     }
37 
38     public void run(){
39         synchronizedObject.printString("b", "bb");
40     }
41 }
42 
43 public class Run {
44     public static void main(String args[]) throws InterruptedException {
45         SynchronizedObject synchronizedObject = new SynchronizedObject();
46         Thread thread = new MyThread(synchronizedObject);
47         thread.start();
48         Thread.sleep(500);
49         thread.stop();
50         System.out.println(synchronizedObject.getName() + "  " + synchronizedObject.getPassword());
51     }
52 }
View Code

輸出結果:

b  aa

因爲stop()方法以及在JDK中被標明爲「過時/做廢」的方法,顯然它在功能上具備缺陷,因此不建議在程序張使用stop()方法。

8. 使用return中止線程

將方法interrupt()與return結合使用也能實現中止線程的效果:

 1 public class MyThread extends Thread {
 2     public void run(){
 3         while (true){
 4             if(this.isInterrupted()){
 5                 System.out.println("線程被中止了!");
 6                 return;
 7             }
 8             System.out.println("Time: " + System.currentTimeMillis());
 9         }
10     }
11 }
12 
13 public class Run {
14     public static void main(String args[]) throws InterruptedException {
15         Thread thread = new MyThread();
16         thread.start();
17         Thread.sleep(2000);
18         thread.interrupt();
19     }
20 }

輸出結果:

...
Time: 1467072288503
Time: 1467072288503
Time: 1467072288503
線程被中止了!

不過仍是建議使用「拋異常」的方法來實現線程的中止,由於在catch塊中還能夠將異常向上拋,使線程中止事件得以傳播。

 

參考:

《java多線程編程核心技術》 高洪巖著

https://www.cnblogs.com/greta/p/5624839.html

相關文章
相關標籤/搜索