1. 前言
停止一个线程更意味着在任务处理完成任务之前停掉正在做的操作,也就是放弃当前的操作。停止一个线程可以用Thread.stop()方法,但最好不要用它。虽然它确实可以停止一个正在运行的线程,但是这个方法是不安全的,而且已被废弃。
在Java中有以下3种方法可以终止正在运行的线程:
使用退出标志,使线程正常退出,也就是当run方法完成后线程终止
使用stop方法强行终止,但是不推荐;因为stop和suspend以及resume一样都是过期作废的方法
使用interrupt方法中断线程
2. 停止不了的线程
interrupt()方法的使用效果并不像for+break语句那样,马上就停止循环,调用interrupt()
方法是在当前线程中打一个停止标志,并不是真的停止线程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class ThreadDemo extends Thread { @Override public void run() { super.run(); for (int i = 0; i < 100000; i++) { System.out.println("i="+(i+1)); } } }
public class TestThreadDemo { public static void main(String[] args) { Thread thread = new ThreadDemo(); thread.start(); try{ Thread.sleep(2000); thread.interrupt(); }catch (Exception ex){ ex.printStackTrace(); } } }
|
输出结果:
1 2 3 4 5 6 7 8
| ... i=99994 i=99995 i=99996 i=99997 i=99998 i=99999 i=100000
|
3. 判断线程是否停止状态
Thread.java类提供了两种方法:
this.interrupted():测试当前线程是否已经中断,当前线程是指运行this.interrput()方法的线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class ThreadDemo extends Thread {
@Override public void run() { super.run(); for (int i = 0; i < 1000000; i++) { i++; } } } public class TestThreadDemo { public static void main(String[] args) { Thread thread = new ThreadDemo(); thread.start(); try { Thread.sleep(2000); thread.interrupt();
System.out.println("stop 1??" + Thread.interrupted()); System.out.println("stop 2??" + Thread.interrupted()); } catch (Exception ex) { ex.printStackTrace(); } } }
|
运行结果:
1 2
| stop 1??false stop 2??false
|
从控制台输出信息可以看出:线程并未停止,这也证明来interrupt()
方法的解释,测试当前线程是否已经中断,这个当前线程是main,它未被中断,所以打印的两个结果都是false;
如何使main线程产生中断效果呢?
1 2 3 4 5 6 7 8 9
| public class TestThreadDemo2 { 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"); } }
|
输出:
1 2 3
| stop 1??true stop 2??false end
|
方法interrupt()
的确判断出当前线程是否停止状态,但是为什么第2个为false呢?
官方帮助文档对interrupt()
方法的解释是:测试当前线程是否已经中断。线程的中断状态有该方法清除。换句话说:如果连续两次调用该方法,则第二次返回false;
this.isInterrupted():测试线程是否已经中断
1 2 3 4 5 6 7 8 9
| public class IsInterruptDemo { public static void main(String[] args) { Thread thread = new ThreadDemo(); thread.start(); thread.interrupt(); System.out.println("stop 1??" + thread.isInterrupted()); System.out.println("stop 2??" + thread.isInterrupted()); } }
|
运行结果:
1 2
| stop 1??true stop 2??true
|
isInterrupted()
并为清除状态,所以打印来两个true
4. 停止线程-异常法
有了前面的知识,就可以在线程中用for来判断线程是否是停止状态,如果是停止状态,则后面的代卖不再运行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class ThreadDemo extends Thread {
@Override public void run() { super.run(); for (int i = 0; i < 500000; i++) { if (Thread.interrupted()) { System.out.println("线程已经终止,for循环不再执行"); break; } System.out.println("i = " + (i + 1)); } System.out.println("这是for循环外的语句,也会被执行"); } } public class TestThreadDemo { public static void main(String[] args) { Thread thread = new ThreadDemo(); thread.start(); try { Thread.sleep(1000); thread.interrupt(); } catch (Exception ex) { ex.printStackTrace(); } } }
|
运行结果:
1 2 3 4 5 6 7 8
| ...... i = 285135 i = 285136 i = 285137 i = 285138 i = 285139 线程已经终止,for循环不再执行 这是for循环外的语句,也会被执行
|
虽然停止了线程,但是如果for语句下面还有语句,还是会继续执行的。如果不需要继续执行
只需要在上面break的地方改成throw new InterruptedException();
即可,这样就会抛出异常。
5. 停止线程-sleep法
如果线程在sleep状态下停止线程,会是什么效果?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class ThreadDemo extends Thread {
@Override 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(); } } } public class TestThreadDemo { public static void main(String[] args) { Thread thread = new ThreadDemo(); thread.start(); try { thread.interrupt(); } catch (Exception ex) { ex.printStackTrace(); } } }
|
运行结果:
1 2 3 4 5
| 线程开始.... 在沉睡中被停止, 进入catch, 调用isInterrupted()方法的结果是:false java.lang.InterruptedException: sleep interrupted trueat java.lang.Thread.sleep(Native Method) trueat org.example.ThreadDemo.run(ThreadDemo.java:13)
|
从打印的结果来看,如果在sleep状态下停止线程,会进入catch语句,并且清除停止状态值,使之变为false。
前一个实验是先sleep然后再用interrupt()挺尸,与之相反的操作如下:
1 2 3 4 5 6 7 8 9
| ....... i=9997 i=9998 i=9999 i=10000 先停止,再遇到sleep 进入catch java.lang.InterruptedException: sleep interrupted trueat java.lang.Thread.sleep(Native Method) trueat org.example.ThreadDemo.run(ThreadDemo.java:16)
|
6. 停止线程-暴力停止
使用stop()方法停止线程是非常暴力的,且方法已经被废弃了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public class ThreadDemo extends Thread { private int i = 0; @Override public void run() { super.run(); try { while (true) { System.out.println("i = " + i); i++; Thread.sleep(200); } } catch (InterruptedException e) { e.printStackTrace(); } } } public class TestThreadDemo { public static void main(String[] args) { Thread thread = new ThreadDemo(); thread.start(); try{ Thread.sleep(2000); thread.stop(); } catch (Exception ex) { ex.printStackTrace(); } } }
|
运行结果:
1 2 3 4 5 6 7 8 9 10 11 12
| 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
|
7. 方法stop()与java.lang.ThreadDeath异常
调用stop()方法时会抛出java.lang.ThreadDeath异常,但是通常情况下,此异常不需要显示地捕捉。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class ThreadDemo extends Thread { @Override public void run() { super.run(); try { this.stop(); } catch (ThreadDeath e) { System.out.println("进入catch"); e.printStackTrace(); } } } public class TestThreadDemo { public static void main(String[] args) { Thread thread = new ThreadDemo(); thread.start(); } }
|
运行结果:
1 2 3 4
| 进入catch java.lang.ThreadDeath trueat java.lang.Thread.stop(Thread.java:853) trueat org.example.ThreadDemo.run(ThreadDemo.java:11)
|
stop()方法已经作废,因为如果强制让线程停止有可能是一些清理性的工作得不到完整,另一种情况就是对锁定的对象进行了解锁,导致数据得不到同步的处理,出现数据不一致的情况。
释放锁的不良后果
使用stop()释放锁将会给数据造成不一致性的结果。如果出现这样的情况,程序处理的数据就有可能遭到破坏,最终导致程序执行的流程错误,一定要特别注意:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| public class SynchronizedObject { private String name = "a"; private String password = "aa";
public synchronized void printString(String name, String password){ try { this.name = name; Thread.sleep(100000); this.password = password; } catch (InterruptedException e) { e.printStackTrace(); } } }
public class MyThread extends Thread { private SynchronizedObject synchronizedObject; public MyThread(SynchronizedObject synchronizedObject){ this.synchronizedObject = synchronizedObject; }
public void run(){ synchronizedObject.printString("b", "bb"); } }
public class TestThreadDemo { public static void main(String args[]) throws InterruptedException { SynchronizedObject synchronizedObject = new SynchronizedObject(); Thread thread = new MyThread(synchronizedObject); thread.start(); Thread.sleep(500); thread.stop(); System.out.println(synchronizedObject.getName() + " " + synchronizedObject.getPassword()); } }
|
输出结果:
由于stop()方法以及在JDK中被标明为“过期/作废”的方法,显然它在功能上具有缺陷,所以不建议在程序张使用stop()方法。
8. 使用return停止线程
将方法interrupt()与return结合使用也能实现停止线程的效果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class ThreadDemo extends Thread { @Override public void run() { while (true) { if (this.isInterrupted()) { System.out.println("线程被停止了"); return; } System.out.println("time:" + System.currentTimeMillis()); } } } public class TestThreadDemo { public static void main(String[] args) { Thread thread = new ThreadDemo(); thread.start(); try { Thread.sleep(2000); thread.interrupt(); } catch (Exception ex) { ex.printStackTrace(); } } }
|
输出结果:
1 2 3 4 5 6 7
| ....... time:1676209667113 time:1676209667113 time:1676209667113 time:1676209667113 time:1676209667113 线程被停止了
|
总结:建议使用“抛异常”的方法来实现线程的停止,因为在catch块中还可以将异常向上抛,使线程停止事件得以传播。