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

为什么强烈不建议使用stop和suspend方法来中断线程?

时间:2023-04-01 15:04:51 Java

我们知道stop、suspend等中断或阻塞线程的方法在java高版本中都被打上了@Deprecated过期标签,那么为什么它们一直登上java的历史舞台并逐渐退出舞台,是人性的扭曲还是道德的沦丧,还是被不思进取所取代?如果他们被取代,那么取代他们的人是谁?1.stop结束首先,stop方法的作用是什么?使用java源码中的一段注释来理解:Forcesthethreadtostopexecution.,即强制线程停止执行。‘力量’似乎已经透露出停止法的含义。残忍无理。那么我们来看看java开发者是如何解释stop被淘汰的:可以看出以下几点:1.stop方法本质上是不安全的2.使用Thread.stop停止一个线程会导致它解锁所有锁定的监视器,即,直接释放当前线程获得的所有锁,使当前线程直接进入阻塞状态。我们以上面提到的两点为例:publicstaticvoidmain(String[]args)throwsInterruptedException{Objecto1=newObject();对象o2=新对象();Threadt1=newThread(()->{synchronized(o1){synchronized(o2){try{System.out.println("t1gettolock");Thread.sleep(5000);System.out.println(“t1结束”);}catch(InterruptedExceptione){e.printStackTrace();}}}});t1.开始();线程.sleep(1000);Threadt2=newThread(()->{synchronized(o1){synchronized(o2){try{System.out.println("t2获得了锁");线程.睡眠(5000);System.out.println("t2结束");}catch(InterruptedExceptione){e.printStackTrace();}}}});t2.开始();t1.停止();}运行结果:可以看到当线程t1获得了o1和o2两个锁开始执行时,在执行结束前,主线程调用了t1的stop方法中断了t1的执行,并释放了t1获得的所有锁t1线程。中断后,t2获得了o1和o2的锁,开始执行直到结束,而t1却死在了sleep中。sleep之后的代码是不Execution的,所以我们在使用stop的时候,不知道线程跑到哪里去了,猛烈的打断线程。如果sleep之后的代码是比较重要的代码,比如资源释放和重要的业务逻辑,或者其他线程依赖t1线程的运行结果,那么直接中断可能会产生严重的后果。所以不推荐使用stop来中断线程。我们应该如何优雅地结束一个线程?我们可以在java开发者的评论中找到解决方法:可以看到java开发者推荐我们使用以下两种方式优雅的停止线程:1.定义一个变量,目标线程会不断的检查这个变量的状态,并在变量达到某个状态时停止线程。代码示例如下:volatilestaticbooleanflag=false;publicstaticvoidmain(String[]args)throwsInterruptedException{Objecto1=newObject();Threadt1=newThread(()->{synchronized(o1){try{System.out.println("t1已获取锁");while(!flag)Thread.sleep(5000);//执行业务逻辑System.out.println("t1结束");}catch(InterruptedExceptione){e.printStackTrace();}}});t1.开始();线程.睡眠(1000);Threadt2=newThread(()->{synchronized(o1){try{System.out.println("t2获取锁");Thread.sleep(5000);//执行业务逻辑System.out.println("t2结束");}catch(InterruptedExceptione){e.printStackTrace();}}});t2.开始();标志=真;}运行结果:2.使用interrupt方法中断线程代码示例如下:publicstaticvoidmain(String[]args)throwsInterruptedException{Objecto1=newObject();Threadt1=newThread(()->{synchronized(o1){System.out.println("t1gettolock");while(!Thread.currentThread().isInterrupted()){for(inti=0;i<100;i++){if(i==50)System.out.println();System.out.print(i+"");}System.out.println();}System.out.println("t1结束");}});t1.开始();Threadt2=newThread(()->{synchronized(o1){try{System.out.println("t2已获取锁");Thread.sleep(5000);//执行业务逻辑System.out.println("t2结束");}catch(InterruptedExceptione){e.printStackTrace();}}});t2.开始();t1.中断();}运行结果:我们使用while(!Thread.currentThread().isInterrupted())不断判断当前线程是否被中断,线程自然死亡,释放锁。可以看到调用interrupt方法后,不会像stop那样暴力中断线程。它会等到当前运行的逻辑结束后,再检查是否被中断,非常优雅。注意:运行示例代码时可能不会打印出该数字。这是因为当t1线程运行到while(!Thread.currentThread().isInterrupted())时,主线程已经调用了interrupt方法,所以多次运行可能会打印出number。二、结束suspendsuspend方法的作用是挂起一个线程,直到调用resume方法恢复线程,但是调用suspend方法后,被挂起的线程获得的锁并不会释放,所以suspend和两兄弟的简历都被打上了容易造成死锁的标签。当然,这也是导致停复牌退出历史舞台的罪魁祸首。同样,我们看看java开发者给出的消除suspend:的理由,从中我们可以得出以下结论:1.suspend有天然的死锁倾向2.当一个线程被挂起时,该线程持有的锁并没有它会被释放,其他线程将无法访问这些资源。3.挂起一个线程后,如果resume过程中出现异常,resume方法执行失败,锁无法释放,导致死锁。接下来模拟什么是suspendDeadlock的场景,说的很便宜,展示我的代码:publicstaticvoidmain(String[]args)throwsInterruptedException{Objecto1=newObject();对象o2=新对象();Threadt1=newThread(()->{synchronized(o1){System.out.println("t1获取到o1锁开始执行");try{Thread.sleep(5000);//模拟执行业务logic}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("t1执行结束");}});t1.开始();Threadt2=newThread(()->{synchronized(o2){System.out.println("t2获取o2并开始执行");try{Thread.sleep(2000);//执行耗时业务}catch(InterruptedExceptione){e.printStackTrace();}synchronized(o1){System.out.println("t2获取o1锁,继续执行");}System.out.println("t2执行结束");}});t2.开始();线程.睡眠(1000);t1.暂停();//假设抛出未知异常inti=1/0;t1.resume();}运行结果:可以看到整个程序卡住了,调用resume恢复t1线程前抛出未知异常,导致t1挂掉无法释放o1锁,t2需要获取o1锁在继续执行之前,却在苦苦等待,然而o1却被t1紧紧的操纵着,整个程序从此陷入了无休止的等待----死锁

最新推荐
猜你喜欢