Java停止线程的三种方式

概述:

好几天没写总结性博客了,这几天状态有点懈怠,你怎么可以这个样子呢😠

这个问题是前几天阿里一面时出现的,当时只是回答了使用stop()方法和使用interrupt()方法两种方式,因为之前看过这个问题但是并没有进行深入,真的是追悔莫及,我懈怠了这个知识,这个只是要也会在一些场合来懈怠我🙂

下面分别来看一下这几种退出方式:

1.正常退出

当线程执行完run()方法之后,就会正常结束,例如:

public void run() {
    while(flag){
        //do something
    }
}

我们设置了退出标志,当flag为false的时候退出while循环,线程也会结束

2.使用stop()方法

这种方法非常暴力,不推荐使用,在多线程情况下可能会出现线程安全问题,当时面试中我回答的是如果当前线程突然被暴力终止,那么可能会会影响其他等待共享资源线程的状态,这种情况是发生在AQS中,但是这不是最主要的,如果一个线程突然“猝死”,那么这个线程会释放掉自己占有的释放锁,而且临界区还没有执行完成,如果我们把临界区中同步的操作当成一个事务来看,因为上锁可以保证这些操作的原子性,但是线程终止时并没有提供回滚操作,问题很大

接下来我们以一个例子看一下,代码引自:为什么Thread.stop()方法被弃用,给我的启示就是以后学习一些知识的时候不要满足于理论进行“脑嗨”,把IDEA打开自己实现一下:

/**
 * 一个展示Thread.stop()方法为什么被弃用的例子
 * @author RJH 
 * @date 2017年12月11日 下午7:48:00
 */
public class StopDemo {

	public static void main(String[] args) {
		StopThread thread=new StopThread();
		thread.start();
		try {
			//休眠1秒,确保i变量自增成功
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			//一般会处理中断异常,这里作为例子就直接打印到控制台了
			e.printStackTrace();
		}
		//暂停线程
		thread.stop();
		while(thread.isAlive()){//确保线程已经终止
		
		}
		//输出结果
		thread.print();
	}
	/**
	 * 展示用的例子,内部对两个整数做自增
	 * @author RJH 
	 * @date 2017年12月11日 下午7:48:32
	 */
	private static class StopThread extends Thread{
	
		private int i=0;
	
		private int j=0;
	
		@Override
		public void run(){
			synchronized (this) {//增加同步锁,确保线程安全
				++i;
				try {
					//休眠10秒,模拟耗时操作
					Thread.sleep(10000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				++j;
			}
		}
		/**
		 * 打印i和j
		 */
		public void print(){
			System.out.println("i="+i+" j="+j);
		}
	}
}

运行结果:

i=1 j=0

如果线程正常运行,执行结束后输出的应该是i = 1 j = 1,但是由于异常终止的原因,只进行了++i操作

3.使用interrupt()方法

这个方法可以看成是一种消息通知机制,通过修改被调用线程的中断状态来告知这个线程

interrupt() 方法只是改变中断状态而已,它不会中断一个正在运行的线程。
这一方法实际完成的是,给受阻塞的线程发出一个中断信号,这样受阻线程就得以退出阻塞的状态。
更确切的说,如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,此时调用该线程的interrupt()方法,那么该线程将抛出一个 InterruptedException中断异常(该线程必须事先预备好处理此异常),从而提早地终结被阻塞状态。如果线程没有被阻塞,这时调用 interrupt()将不起作用,直到执行到wait(),sleep(),join()时,才马上会抛出 InterruptedException。