何爲終止線程?
停止一個線程意味着在任務處理完任務之前停掉正在做的操作,也就是放棄當前的操作。
終止線程的方法
①使用退出標誌,使線程正常退出,也就是當run方法完成後線程終止。
②使用stop方法強行終止,但是不推薦這個方法,因爲stop和suspend及resume一樣都是過期作廢的方法。
③使用interrupt方法中斷線程。
④使用return返回。
判斷線程是否停止狀態
Thread.java類中提供了兩種方法:
this.interrupted(): 測試當前線程是否已經中斷;
this.isInterrupted(): 測試線程是否已經中斷;
上面兩個方法有一些區別。
使用isInterrupted方法測試線程是否已經中斷
public class Run {
public static void main(String args[]) throws InterruptedException {
Thread thread = new MyThread();
thread.start();
thread.interrupt();
System.out.println("stop 1??" + thread.isInterrupted()); //true
System.out.println("stop 1??" + Thread.currentThread().isInterrupted()); //false
}
}
public class MyThread extends Thread {
public void run(){
super.run();
for(int i=0; i<5000000; i++){
i++;
// System.out.println("i="+(i+1));
}
}
}
上面的代碼中對thread線程進行終止,main線程沒有終止,所以輸出一個true,一個false。
停止線程的方法:thread.interrupt()
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();
}
}
}
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));
}
}
}
輸出結果:
i=133837
i=133838
i=133839
i=133840
線程已經終止, for循環不再執行
上面的代碼中,雖然線程如期停止,但是如果在run方法的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));
}
System.out.println("這是for循環外面的語句,也會被執行");
}
}
執行結果:
i=137682
i=137683
i=137684
線程已經終止, for循環不再執行
這是for循環外面的語句,也會被執行
如何解決上述問題,我們可以在this.interrupted()爲true時,拋出一個異常,通過異常來終止線程繼續向下進行。
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();
}
}
}
執行結果:
i=152395
i=152396
i=152397
線程已經終止, for循環不再執行
進入MyThread.java類中的catch了。。。
java.lang.InterruptedException
at com.coach.javainterview.thread.MyThread.run(Run.java:23)
如果線程在sleep()狀態下interrupt線程,會是什麼效果呢?
public class Run {
public static void main(String args[]){
Thread thread = new MyThread();
thread.start();
try {
Thread.sleep(2000); //main線程先sleep2秒
thread.interrupt(); //對thread線程進行interrupt
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MyThread extends Thread {
public void run(){
super.run();
try {
System.out.println("線程開始。。。");
Thread.sleep(200000);
System.out.println("線程結束。");
} catch (InterruptedException e) {
System.out.println("在沉睡中被停止, 進入catch, 調用isInterrupted()方法的結果是:" + this.isInterrupted());
e.printStackTrace();
}
}
}
執行結果:
線程開始。。。
在沉睡中被停止, 進入catch, 調用isInterrupted()方法的結果是:false
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.coach.javainterview.thread.MyThread.run(Run.java:22)
如果線程在interrupt()狀態下停止線程,會是什麼效果呢?
public class Run {
public static void main(String args[]){
Thread thread = new MyThread();
thread.start();
thread.interrupt();
}
}
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); //interrupt狀態時讓線程sleep
System.out.println("線程結束。");
} catch (InterruptedException e) {
System.out.println("先停止,再遇到sleep,進入catch異常");
e.printStackTrace();
}
}
}
運行結果:
i=9997
i=9998
i=9999
先停止,再遇到sleep,進入catch異常
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.coach.javainterview.thread.MyThread.run(Run.java:20)
停止線程的方法:使用stop方法
public class Run {
public static void main(String args[]) throws InterruptedException {
Thread thread = new MyThread();
thread.start();
Thread.sleep(2000);
thread.stop();
}
}
class MyThread extends Thread {
private int i = 0;
public void run(){
super.run();
try {
while (true){
System.out.println("i=" + i);
i++;
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
執行結果:
爲什麼不推薦使用stop方法來停止線程?
原因一:
stop方法是過時的
從Java編碼規則來說,已經過時的方式不建議採用.
原因二:
stop方法會導致代碼邏輯不完整
stop方法是一種"惡意" 的中斷,一旦執行stop方法,即終止當前正在運行的線程,不管線程邏輯是否完整,這是非常危險的.
public class Client {
public static void main(String[] args) throws Exception {
// 子線程
Thread thread = new Thread() {
@Override
public void run() {
try {
// 該線程休眠1秒
Thread.sleep(1000);
} catch (InterruptedException e) {
//異常處理
}
System.out.println("此處代碼不會執行");
}
};
// 啓動線程
thread.start();
// 主線程休眠0.1秒
Thread.sleep(100);
// 子線程停止
thread.stop();
}
}
上面的代碼中,我們讓子線程thread啓動後休眠1秒,在此線程休眠的同時,我們強行使用stop方法來停止thread子線程,在println中可能是要輸出一些很重要的信息,比如子線程的主邏輯,資源回收,情景初始化等等,但是因爲調用了stop方法,後面的println方法不會被調用。
這是極度危險的,因爲我們不知道子線程會在什麼時候停止,stop連基本的邏輯完整性都無法保證,而且此種操作也是非常隱蔽的,子線程執行到何處會被關閉很難定位,這爲以後的維護帶來了很多的麻煩.
原因三:
stop方法會破壞原子邏輯
public class Client {
public static void main(String[] args) {
MultiThread t = new MultiThread();
Thread t1 = new Thread(t);
// 啓動t1線程
t1.start();
for (int i = 0; i < 5; i++) {
new Thread(t).start();
}
// 停止t1線程
t1.stop();
}
}
class MultiThread implements Runnable {
int a = 0;
@Override
public void run() {
// 同步代碼塊,保證原子操作
synchronized ("") {
// 自增
a++;
try {
// 線程休眠0.1秒
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 自減
a--;
String tn = Thread.currentThread().getName();
System.out.println(tn + ":a =" + a);
}
}
}
MultiThread實現了Runnable接口,具備多線程的能力,run方法中加入了synchronized代碼塊,表示內部是原子邏輯,a的值會先增加後減少,按照synchronized的規則,無論啓動多少個線程,打印出來的結果都應該是a=0。
但是如果有一個正在執行的線程被stop,就會破壞這種原子邏輯.(上面main方法中代碼)
首先說明的是所有線程共享了 一個MultThread的實例變量t,其次由於在run方法中加入了同步代碼塊,所以只能有一個線程進入到synchronized塊中。
此段代碼的執行順序如下:
1)線程t1啓動,並執行run方法,由於沒有其他線程同步代碼塊的鎖,所以t1線程執行自加後執行到sleep方法開始休眠,此時a=1.
2)JVM又啓動了5個線程,也同時運行run方法,由於synchronized關鍵字的阻塞作用,這5個線程不能執行自增和自減操作,等待t1線程釋放線程鎖.
3)主線程執行了t1.stop方法,終止了t1線程,注意由於a變量是線程共享的,所以其他5個線程獲得的a變量也是1.
4)其他5個線程獲得CPU的執行機會,打印出a的值.
上面代碼的執行結果是:
Thread-4:a =1
Thread-1:a =1
Thread-2:a =1
Thread-3:a =1
Thread-5:a =1
停止線程的方法:使用return
class MyThread extends Thread {
public void run(){
while (true){
if(this.isInterrupted()){
System.out.println("線程被停止了!");
return;
}
System.out.println("Time: " + System.currentTimeMillis());
}
}
}
public class Run {
public static void main(String args[]) throws InterruptedException {
Thread thread = new MyThread();
thread.start();
Thread.sleep(2000);
thread.interrupt();
}
}
執行結果:
Time: 1516708475697
Time: 1516708475697
Time: 1516708475697
Time: 1516708475697
線程被停止了!