JAVA中有3种方式可以终止正在运行的线程
①线程正常退出,即run()方法执行完毕了
②使用Thread类中的stop()方法强行终止线程。但stop()方法已经过期了,不推荐使用
③使用中断机制interrupt()
1.stop()方法
stop()在java多线程中已经废弃
1.stop()方法会导致释放锁的不良后果,数据不完整
比如一个上锁了得方法:
threadA线程拥有了监视器,这些监视器负责保护某些临界资源,比如说银行的转账的金额。当正在转账过程中,main线程调用 threadA.stop()方法或者this.stop()。结果导致监视器被释放,其保护的资源(转账金额)很可能出现不一致性。比如,A账户减少了100,而B账户却没有增加100,没有保证数据原子性
2.当线程调用stop()方法时,会立刻终止线程的所有操作,并抛出ThreadDeath异常,通常不需要捕捉
public class StopTest {
static class MyThread extends Thread{
@Override
public void run() {
synchronized(this){
System.out.println("A转给B账户1000元");
currentThread().stop();
System.out.println("B收到A转给的1000元");
}
}
}
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
}
}
A转给B账户1000元
2.interrupt() 中断机制
-
判断线程是否被中断
interrupt()不会真的中断一个正在运行的线程,而是给线程打一个停止的标记,还需要配合判断来优雅的终止线程
public class InterruptTest {
static class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 500; i++) {
System.out.println("count="+i);
}
}
}
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
t1.interrupt();
System.out.println("调用线程中断方法");
}
}
调用线程中断方法
count=0
count=1
count=2
count=3
count=4
count=5
count=6
interrupt() 并不会真正的停止正在运行的线程
3.判断线程的停止状态
interrupted() 是静态方法 而 isInterrupted() 是实例方法
interrupted()方法:判断当前线程是否已经中断
isInterrupted()方法:判断线程是否已经中断
public class InterruptTest {
static class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 500000; i++) {
System.out.println("count="+i);
}
}
}
public static void main(String[] args) {
try {
MyThread t1 = new MyThread();
t1.start();
Thread.sleep(1000);
t1.interrupt();
System.out.println("线程是否中断:"+t1.interrupted());
System.out.println("线程是否中断:"+t1.interrupted());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
count=186950
count=186951
count=186952
count=186953
线程是否中断:false
线程是否中断:false
count=186954
count=186955
前面调用t1中断线程,后面判断是否中断,结果两次全为false未中断,说明interrupted()方法判断的当前线程不是t1而是main线程
修改上面的代码,中断main线程再判断
public class InterruptTest {
static class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100000; i++) {
System.out.println("count="+i);
}
}
}
public static void main(String[] args) {
try {
MyThread t1 = new MyThread();
t1.start();
Thread.sleep(100);
Thread.currentThread().interrupt();
System.out.println("线程是否中断:"+t1.interrupted());
System.out.println("线程是否中断:"+t1.interrupted());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
count=11429
count=11430
线程是否中断:true
线程是否中断:false
count=11431
count=11432
count=11433
结果第一个返回时true,第二个返回时false 说明interrupted()会调用 isInterrupted 方法 传入 参数clean 状态,调用该方法,该方法内部会清除中断状态
如下源码所示
/**
* Tests if some Thread has been interrupted. The interrupted state
* is reset or not based on the value of ClearInterrupted that is
* passed.
*/
private native boolean isInterrupted(boolean ClearInterrupted);
修改上面的代码 改为isInterrupted()判断
public class InterruptTest {
static class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100000; i++) {
System.out.println("count="+i);
}
}
}
public static void main(String[] args) {
try {
MyThread t1 = new MyThread();
t1.start();
Thread.sleep(100);
t1.interrupt();
System.out.println("线程是否中断:"+t1.isInterrupted());
System.out.println("线程是否中断:"+t1.isInterrupted());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
count=12205
count=12206
线程是否中断:true
线程是否中断:true
count=12207
count=12208
count=12209
结果返回两个true证明isInterrupted()并没有清除状态标志。
-
终止java线程
上面的示例说明线程的interrupt()方法并不能立即终止线程,那么如果我们要中断该线程怎么办呢
public class InterruptTest2 {
static class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 500000; i++) {
System.out.println("count="+i);
if(this.interrupted()){ //判断当前线程是否被中断
System.out.println("main线程命令中断线程");
break;
}
}
System.out.println("循环中断后。。。");
}
}
public static void main(String[] args) {
try {
MyThread t1 = new MyThread();
t1.start();
Thread.sleep(2000);//main线程休眠2秒
t1.interrupt(); //中断该线程
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
count=384552
count=384553
count=384554
count=384555
count=384556
main线程命令中断线程
循环中断后。。。
上诉代码运行结果可以看出当循环内判断该线程已经中断后,就通过break跳出循环。同时 最后一句循环中断后也会继续输出,因为break只是跳出该循环,并没有结束该线程。
修改上面的代码将break换成return
static class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 500000; i++) {
System.out.println("count="+i);
if(this.interrupted()){ //判断当前线程是否被中断
System.out.println("main线程命令中断线程");
return;
}
}
System.out.println("循环中断后。。。");
}
}
count=364980
count=364981
count=364982
count=364983
main线程命令中断线程
此时最后一句话确实没有执行。
但是使用return也不好,因为无法将事件传播
优雅的解决
抛异常方法 抛出InterruptedException异常
修改上诉代码
public class InterruptTest2 {
static class MyThread extends Thread{
@Override
public void run() {
try {
for (int i = 0; i < 500000; i++) {
System.out.println("count="+i);
if(this.interrupted()){ //判断当前线程是否被中断
System.out.println("main线程命令中断线程");
throw new InterruptedException();
}
}
System.out.println("循环中断后。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
try {
MyThread t1 = new MyThread();
t1.start();
Thread.sleep(2000);//main线程休眠2秒
t1.interrupt(); //中断该线程
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
count=380876
count=380877
count=380878
count=380879
main线程命令中断线程
java.lang.InterruptedException
at com.xuxu.b_thread_interrupt.InterruptTest2$MyThread.run(InterruptTest2.java:13)
java.lang.InterruptedException
at com.xuxu.b_thread_interrupt.InterruptTest2$MyThread.run(InterruptTest2.java:13)
上面就是一个采用抛出异常的方式来结束线程的示例。尽管该示例的实用性不大。原因是我们用e.printStackTrace();生吞了中断
由于this.interrupted()在第一次调用后清楚中断状态为false。仅仅记录 InterruptedException 也不是明智的做法,因为等到人来读取日志的时候,再来对它作出处理就为时已晚了。
有时候抛出 InterruptedException 并不合适,例如当由 Runnable 定义的任务调用一个可中断的方法时,就是如此。在这种情况下,不能重新抛出 InterruptedException,但是您也不想什么都不做。当一个阻塞方法检测到中断并抛出 InterruptedException 时,它清除中断状态。如果捕捉到 InterruptedException 但是不能重新抛出它,那么应该保留中断发生的证据,以便调用栈中更高层的代码能知道中断,并对中断作出响应。该任务可以通过调用 interrupt() 以 “重新中断” 当前线程来完成
2.因为,run方法是实现的Runnable接口中的方法。不能像下面这样定义,也即上面所说的:“不能重新抛出InterruptedException”。
@Override
public void run() throws InterruptedException{//这是错误的
//do something...
将以上改为Thread.currentThread().interrupt();
static class MyThread extends Thread{
@Override
public void run() {
try {
for (int i = 0; i < 500000; i++) {
System.out.println("count="+i);
if(this.interrupted()){ //判断当前线程是否被中断
System.out.println("main线程命令中断线程");
throw new InterruptedException();
}
}
System.out.println("循环中断后。。。");
} catch (InterruptedException e) {
// e.printStackTrace();
Thread.currentThread().interrupt();//这样处理比较好
}
}
}
这样,就由 生吞异常 变成了 将 异常事件 进一步扩散了。 保留了中断的证据
ps
对于可取消的阻塞状态中的线程, 比如等待在这些函数上的线程, Thread.sleep(), Object.wait(), Thread.join(), 这个线程收到中断信号interrupt()后, 会抛出InterruptedException。就不需要手动抛异常法了,只需要在捕获异常块再次标记中断即可