这篇文章是学习如何优雅地学习挂起线程?通过对Java线程的生老病死的学习,相信大家对线程的运行和线程的状态都有了一定的了解,那么现在就来学习一下线程挂起:错误的线程挂起-先停到解释一个错误的停止线程的方法——stop:停止线程并清除监听锁信息,但是可能会导致线程安全问题,JDK不推荐使用,类似的方法还有destroy,因为JDK从来没有实现过这个方法,这里就不介绍了。下面我们通过一个程序来解释为什么stop会导致线程安全问题。首先定义一个线程类StopThread:publicclassStopThreadextendsThread{privateinti=0;privateintj=0;@Overridepublicvoidrun(){synchronized(this){//增加同步锁保证线程安全++i;try{//休眠10秒,模拟耗时操作Thread.sleep(10000);}catch(InterruptedExceptione){e.printStackTrace();}++j;}}/***打印i和j*/publicvoidprint(){System.out.println("i="+i+"j="+j);}}这个线程做的是自动执行同步代码块中的两个变量i和j的自增操作,但是在这个执行过程中会休眠10秒过程。如果在这个过程中线程被stop方法终止,那么i和j的数据就会不正确,也可以说是程序设计中的线程安全问题,因为主线程影响创建的StopThread线程的数据不准确,理想的正确输出结果应该是要么全部加成功,要么全部加失败,因为我们加锁的目的是为了保证操作的原子性或者让两个变量在运行过程中不受其他线程的干扰。下面编写StopThreadDemo类,使用stop方法进行错误演示:publicclassStopThreadDemo{publicstaticvoidmain(String[]args)throwsInterruptedException{StopThreadthread=newStopThread();thread.start();//休眠1秒,确保i变量成功递增Thread.sleep(1000);//挂起线程thread.stop();//错误终止while(thread.isAlive()){//确保线程已经终止}//输出结果线程.print();}}在StopThreadDemo类中,创建并启动了StopThread线程。这个线程就是执行变量i和j的自增操作,但是这个自增操作是用synchronization关键字包裹的同步代码块。这样做是为了让两个变量的自增操作实现原子性,不会受到其他线程的干扰,保证线程安全。但是在线程休眠的10秒内,通过stop方法停止线程,会发现输出结果是i=1j=0,也就是前半段代码实现了i的自增,但j自增的后半部分失败了。会导致线程中的数据不一致,从而达不到同步代码块原子性的目的,破坏了线程安全。正确的线程挂起-中断介绍完错误的挂起方式,让我们学习正确的线程挂起-中断:如果目标线程正在调用Object类的wait()、wait(long)或wait(long,int)方法,join(),join(long,int)或sleep(long,int)方法被阻塞,则中断生效,该线程的中断状态将被清除,并抛出InterruptedException。如果目标线程被IO或者NIO中的Channel阻塞,同样的IO操作会被中断,返回一个特殊的异常值,达到中止线程的目的。如果以上条件都不满足,则设置本线程的中断状态。接下来把StopThreadDemo中的stop改成interrupt看看运行结果是什么:java.lang.InterruptedException:sleepinterruptedatjava.lang.Thread.sleep(NativeMethod)atcom.wupx.thread.StopThread.run(StopThread.java:18)如果我=1j=1,可以发现两个变量的自增可以正常执行,保证了执行的数据一致性。中断不会强制停止,直接中断线程,但是会抛出异常通知我们,开发者可以控制采集。检测到异常后的执行逻辑,使整个程序处于线程安全状态。这是目前JDK版本推荐的中断方式。除了中断的正确方法外,还可以通过标志的形式终止线程:correctthreadsuspension-flag如果代码程序逻辑是循环执行业务,可以在程序执行过程中给线程代码加上标志,比如下面的代码中,程序是在while循环中执行的,通过flag来控制程序是否继续执行。如果在外部线程中将标志更改为false,则创建的子线程代码会接收到这个数据的变化,并将这个变量以通知的形式传递给另一个线程,从而达到控制挂起的效果线程。java.lang.InterruptedException:sleepinterruptedatjava.lang.Thread.sleep(NativeMethod)atcom.wupx.thread.StopThread.run(StopThread.java:18)i=1j=1通过运行代码,结果如下如下:runningrunning运行程序结束的方式受限于线程中执行的业务逻辑。如果程序中有条件可以作为标志,可以这样做,也是终止线程的正确方式。综上所述,本文主要学习线程终止的三种方式:stop、interrupt和flag。你学会了吗?欢迎留言讨论。
