线程停止在Java中是一项很有技巧性的操作,虽然看起来很简单,但是我们必须做好防范措施,以便达到预期的效果。
例如,停止一个线程可以直接用Thread.stop()方法,虽然它可以停止一个正在运行的线程,但是这个方法是不安全的,而且它是已经被弃用的方法。所以不建议使用该方法。大多数时候,停止一个线程使用Thread.interrupt()方法,这里的停止,并不是终止。
在Java中有三种方法可以终止正在运行的线程
1:使用退出标志,使线程正常退出,就是run()方法完成之后线程终止。
2:使用stop()方法强制终止线程,但是这个方法是过期的,可能会产生不可预料的后果,所以不推荐使用这个方法。
3:使用interrupt()方法中断线程。
使用退出标志使线程正常退出
class My_Thread implements Runnable{ private boolean flag = true; @Override public void run() { int i = 1; while(flag){ try{ Thread.sleep(100); System.out.println(Thread.currentThread().getName()+" "+i); i++; }catch (Exception e){ e.printStackTrace(); } } } public void setFlag(boolean flag) { this.flag = flag; } } public class Main{ public static void main(String[] args)throws Exception{ My_Thread New_Thread = new My_Thread(); Thread thread = new Thread(New_Thread,"新线程1"); thread.start(); Thread.sleep(2000); New_Thread.setFlag(false); } }
这种方法很好理解:在线程的运行过程中直接改变run()方法中的标记,使run()方法执行完毕,从而安全退出线程。
使用stop()方法暴力停止线程
class MyThread extends Thread{ private int i = 0; @Override public void run() { try{ while(true){ i++; System.out.println("i="+i); Thread.sleep(1000); } }catch (InterruptedException e){ e.printStackTrace(); } } } public class Main{ @SuppressWarnings("all") public static void main(String[] args){ try{ MyThread myThread = new MyThread(); myThread.start(); Thread.sleep(5000); myThread.stop(); }catch (InterruptedException e){ e.printStackTrace(); } } }
我们可以发现线程被暴力停止之后运行图标变为灰色,线程直接退出了。
stop()会解除线程获取的所有锁定,如果在一个线程对象上调用stop()方法时,这个线程对象所运行的线程就会立即停止,stop()的不安全就体现在这里:假如有线程正在执行synchronized方法的赋值操作时,赋值执行到一半突然被stop了,锁被直接释放,这样就会产生不完整的残废数据,最终导致程序执行的流程错误。
我这里只是演示一下stop方法,在JDK中stop方法被注明是过期的方法,所以不建议在程序中使用stop方法。
interrupt()方法:
interrupt()方法仅仅是在当前的线程中打了一个停止的标记,并不是真正的停止线程,而且interrupt()方法不像break那样马上就停止循环。
class MyThread extends Thread{ private boolean flag = true; @Override public void run() { int i = 1; while(flag){ try{ Thread.sleep(1000);//因为是sleep引起的阻塞,所以线程调用了interrupt方法 //清除了中断标志,抛出了一个InterruptedException异常 boolean bool = Thread.currentThread().isInterrupted(); if(bool){ System.out.println("非阻塞情况下 线程状态:"+bool); break; } System.out.println(i+" :"+Thread.currentThread().getName()); i++; }catch (InterruptedException e){ //中断标志被清除,并且设置为false System.out.println("退出"); boolean bool = Thread.currentThread().isInterrupted(); System.out.println(bool); return;//退出run方法,终端进程 } } } } public class Main{ public static void main(String[] args)throws InterruptedException{ MyThread myThread = new MyThread(); Thread thread = new Thread(myThread,"子线程1"); thread.start(); Thread.sleep(2000); thread.interrupt(); System.out.println("结束"); } }
interrput()方法只是改变中断状态而已,不会中断一个正在运行的线程。这个方法完成的是,给受阻塞的线程发一个中断信号,这样受阻塞的线程就可以退出阻塞的状态。
这里要注意的是:如果线程当前没有被阻塞,那么仅仅是线程的中断标志被修改为true而已,但是如果线程处于阻塞状态,那么再将中断标志设置成true后,还会有以下操作:
如果是wait,sleep或者jion三个方法引起的阻塞,那么线程的中断标志会被重新设置成false,并且抛出一个异常,就是我上面代码的那个例子。大概的意思就是:如果线程在运行中,中断标志设置为true后,一旦线程调用了以上的三种方法的任一种,线程就会立刻抛出一个异常,并且中断标志被擦除,重新设置为false。
在介绍这个方法之后,要再介绍两种方法:interrupted(),isinterrupted()
1:interrupted()方法:测试当前线程是否已经中断。
2:isinterrupted()方法:测试线程是否已经中断。
public static boolean interrupted() { return currentThread().isInterrupted(true); } public boolean isInterrupted() { return isInterrupted(false); }
这两个方法还是有区别的,首先看一下this.interrupt()的解释:测试当前线程是否已经中断,当前线程是指在运行this.interrupt()方法的线程,我先用一段代码来示例。
public class Main{ @SuppressWarnings("all") public static void main(String[] args){ Thread.currentThread().interrupt(); System.out.println("当前线程是否终止:"+Thread.interrupted()); System.out.println("当前线程是否终止:"+Thread.interrupted()); } }
这段代码的意思是终止主方法并判断其是否中断,但是连续调用两次interrupted方法的结果并不一样,原因是interrupted方法具有擦除状态的功能。所以才会一次true一次false。
但是,isinterrupted方法在用于这种情况时结果是这样的
class MyThread extends Thread{ @Override public void run() { for(int i = 0;i<5;i++){ System.out.println(i); } } } public class Main{ @SuppressWarnings("all") public static void main(String[] args){ Thread New_Thread = new MyThread(); New_Thread.start(); New_Thread.interrupt(); System.out.println("当前线程是否终止:" + New_Thread.isInterrupted()); System.out.println("当前线程是否终止:" + New_Thread.isInterrupted()); } }
最后,我们来看一下这两种方法的解释
this.interrupted():测试当前线程是否处于中断状态,执行之后将状态标志清除为false
this.isInterrupted():测试线程对象是否处于中断状态,不会清除状态标志。
我就先总结到这里,我要看运动会了......