当前位置: 首页 > 后端技术 > Java

仍然不知道如何在Java中终止线程?来来来,这篇文章给大家揭秘

时间:2023-04-01 18:36:03 Java

简介我们在工作中经常会用到线程。通常我们让线程执行然后就完成了。你有想过怎么做吗?终止正在运行的线程怎么样?今天就带大家来看看。Thread.stopDisabledMystery询问如何终止线程,可能大多数人都知道可以调用Thread.stop方法。但是jdk1.2之后就不推荐这种方法了,为什么不推荐呢?我们先看看这个方法的定义:@Deprecated(since="1.2")publicfinalvoidstop(){@SuppressWarnings("removal")SecurityManagersecurity=System.getSecurityManager();如果(安全!=null){checkAccess();如果(这!=Thread.currentThread()){security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);}}//零状态值对应于“NEW”,它不能更改为//not-NEW因为我们持有锁。如果(线程状态!=0){恢复();//如果线程被挂起,则唤醒它;no-opotherwise}//VM可以处理所有线程状态stop0(newThreadDeath());}从代码中可以看出,stop方法首先检查是否有线程访问权限。如果有权限,判断当前线程是否是新创建的线程,如果不是,则调用resume方法释放线程的挂起状态。最后调用stop0方法结束线程。其中,resume和stop0是两个native方法,具体实现这里不再赘述。看来stop方法是合理的,没有问题。那么为什么说这种方法不安全呢?接下来我们看一个例子。我们创建一个NumberCounter类,它有一个increaseNumber的安全方法,用来给数字加一:publicclassNumberCounter{//要保存的数字privatevolatileintnumber=0;//数字计数器的逻辑是否完整privatevolatilebooleanflag=false;publicsynchronizedintincreaseNumber()throwsInterruptedException{if(flag){//逻辑不完整thrownewRuntimeException("逻辑不完整,计数未执行");}//开始执行逻辑flag=true;//做一些事情Thread.sleep(5000);数字++;//标志=假;返回号码;}}其实在实际中,这样的方法可能会执行很长时间,所以这里调用Thread.sleep来模拟这个耗时操作。这里我们还有一个flag参数来标记increaseNumber方法是否执行成功。好吧,我们在一个线程中调用这个类的方法看看会发生什么:线程thread=newThread(()->{while(true){try{numberCounter.increaseNumber();}catch(InterruptedExceptione){e.printStackTrace();}}});thread.start();线程.睡眠(3000);线。停止();numberCounter.increaseNumber();}这里,我们创建了一个线程,线程运行3秒后,直接调用thread.stop方法,发现如下异常:Exceptioninthread"main"java.lang.RuntimeException:Thelogicisincomplete,com.flydean.NumberCounter.increaseNumber(NumberCounter.java:12)atcom.flydean.Main.main(Main.java:18)没有执行数字计数器这是因为线程。stop方法直接终止线程的运行,导致mberCounter.increaseNumber执行不完全。然而,这种未完成的状态是隐藏的。如果使用thread.stop方法终止线程,可能会导致未知的结果。因此,我们说thread.stop是不安全的。怎么可能安全?那么,如果不调用thread.stop方法,如何安全终止线程呢?所谓安全,就是线程中的逻辑需要完整执行,而不是只执行一半。为了实现这种效果,Thread为我们提供了三个类似的方法,它们是interrupt、interrupted和isInterrupted。interrupt是为线程设置中断标志;interrupted是检测中断,清除中断状态;isInterrupted仅检测中断。还有一点很重要,interrupted是一个作用于当前线程的类方法,interrupt和isInterrupted作用于这个线程,也就是代码中调用这个方法的实例所代表的线程。Interrupt即中断方法,其工作流程如下:如果当前线程实例正在调用Object类的wait()、wait(long)或wait(long,int)方法或join()、join(long),join(long,int)方法,或者在本实例中调用Thread.sleep(long)或Thread.sleep(long,int)方法处于阻塞状态时,其中断状态将被清除,并抛出InterruptedException被接收。如果此线程在InterruptibleChannel上的I/O操作中被阻塞,通道将关闭,线程的中断状态将设置为true,并且线程将收到java.nio.channels.ClosedByInterruptException。如果这个线程在java.nio.channels.Selector中被阻塞,线程的中断状态将被设置为true,它会立即从select操作中返回。如果以上条件都不成立,则将中断状态设置为真。在上面的例子中,在NumberCounter的increaseNumber方法中,我们调用了Thread.sleep方法,那么如果此时调用线程的interrupt方法,线程就会抛出InterruptedException。让我们将上面的调用示例更改为以下内容:publicstaticvoidmain(String[]args)throwsInterruptedException{NumberCounternumberCounter=newNumberCounter();线程thread=newThread(()->{while(true){try{numberCounter.increaseNumber();}catch(InterruptedExceptione){System.out.println("CatchInterruptedException");thrownewRuntimeException(e);}}});thread.start();线程.睡眠(500);线程中断();numberCounter.increaseNumber();}运行后再试:Exceptioninthread"main"Exceptioninthread"Thread-0"java.lang.RuntimeException:Thelogicisincomplete,thenumbercounterhasnotbeenexecutedatcom.flydean.NumberCounter.increaseNumber(NumberCounter.java:12)在com.flydean.Main2.main(Main2.java:21)java.lang.RuntimeException:java.lang.thread.interrupt:sleepinterruptedatcom.flydean.Main2.lambda$main$0(Main2.java:13)atjava.base/java.lang.Thread.run(Thread.java:833)Causedby:java.lang.InterruptedException:sleepinterruptedatjava.base/java.lang.Thread.sleep(NativeMethod)在com.flydean.NumberCounter.increaseNumber(NumberCounter.java:17)在com.flydean.Main2.lambda$main$0(Main2.java:10)...1个捕获InterruptedException可以看到,我们捕获了这个InterruptedException,了解到具体原因是sleepinterrupted捕获异常后的处理。从上面的分析我们可以知道thread.stop和thread.interrupt的执行机制是不一样的。thread.stop属于静默终止,我们的程序是不知道的,所以会导致数据不一致,从而导致一些未知的异常。而thread.interrupt会显示InterruptedExceptionthrown。当我们捕获到这个异常时,我们就知道线程中的逻辑在执行过程中受到了外部影响的干扰,那么我们可以进行一些数据恢复或者数据校准。测试动作。在上面的代码中,我们捕获了这个异常,打印出了异常日志,然后向上抛出了一个RuntimeException。一般情况下,我们捕获到异常后需要做一些处理。那么自己处理完这个异常是不是就完美了呢?答案是否定的。因为如果我们自己处理这个InterruptedException,那么如果程序的其他部分依赖这个InterruptedException,就可能会出现数据不一致的情况。所以我们自己处理InterruptedException之后,需要再次抛出这个异常。如何抛出InterruptedException异常?有两种方式,第一种是调用Thread.interrupted()清除中断标志后立即抛出:if(Thread.interrupted())//清除中断状态!抛出新的中断异常();另一种方式是,捕获到异常后,再次调用Thread.currentThread().interrupt()中断线程。publicvoidrun(){try{while(true){//dostuff}}catch(InterruptedExceptione){LOGGER.日志(级别。警告,“中断!”,e);//恢复中断状态...Thread.currentThread().interrupt();}}这两种方法都可以达到想要的效果。总结线程不能调用stop终止主要是因为不会抛出异常,会导致一些安全和数据不一致的问题。因此,最好的办法就是调用中断方法来处理。本文示例https://github.com/ddean2009/learn-java-base-9-to-20/tree/master/how-to-stop-thread更多文章请看www.flydean.com