我们知道像stop、suspend这样的中断或者阻塞线程的方法在java高版本中已经打上了@Deprecated过期标签,那么为什么它们一直登上java的历史舞台并逐渐推出舞台呢?是人性的扭曲还是道德的沦丧,还是因为没有攻击性而被取代?如果他们被取代,那么谁来取代他们?1.stop结束首先,stop方法的作用是什么?使用java源码中的一段注释来理解:Forcesthethreadtostopexecution.,即强制线程停止执行。‘力量’似乎已经透露出停止法的含义。残忍无理。那我们看看java开发者是如何解释stop被淘汰的:这种方法本质上是不安全的。使用Thread.stop停止线程会导致它解锁它已锁定的所有监视器(作为uncheckedThreadDeath异常向上传播堆栈的自然结果)。如果之前受这些监视器保护的任何对象处于不一致状态,则损坏的对象对其他线程可见,可能导致任意行为。许多使用stopacplshou修改一些变量来指示目标线程应该停止运行。目标线程应该定期检查这个变量,如果变量指示它要停止运行,则从它的run方法有序地返回。如果目标线程等待很长时间(例如,在条件变量上),则应使用中断方法来中断等待。从这里我们可以看出:stop这个方法本质上是不安全的使用Thread.stop停止线程会导致它Unlockalllockedmonitors,即直接释放当前线程获得的所有锁,使当前线程直接进入阻塞状态我们先来看看上面提到的两点:publicstaticvoidmain(String[]args)throwsInterruptedException{Objecto1=newObject();对象o2=新对象();Threadt1=newThread(()->{synchronized(o1){synchronized(o2){try{System.out.println("t1获得锁");Thread.sleep(5000);System.out.println("t1结束");}catch(InterruptedExceptione){e.printStackTrace();}}}});t1.开始();线程.睡眠(1000);Threadt2=newThread(()->{synchronized(o1){synchronized(o2){try{System.out.println("t2获得了锁");Thread.sleep(5000);System.out.println("t2结束");}catch(InterruptedExceptione){e.printStackTrace();}}}});t2.开始();t1.停止();}运行结果:可以看到,当线程t1获取到o1和o2的两把锁开始执行时,在执行结束前,主线程调用t1的stop方法中断t1的执行,释放所有的t1线程获得的锁,中断后t2获得了o1和o2的锁,开始执行直到结束,但是t1死在了sleep中,sleep之后的代码没有执行。因此,我们在使用stop的时候不知道线程跑到哪里去了,线程被暴力中断了。如果sleep之后的代码是资源释放、重要业务逻辑等比较重要的代码,或者其他线程依赖t1线程的运行结果,直接中断可能会造成严重的后果。所以不推荐使用stop来中断线程。我们应该如何优雅地结束一个线程?我们可以从java开发者的评论中窥见一个解决方案:很多stop的用法应该换成简单修改一些变量的代码来表示目标线程应该停止运行。目标线程应该定期检查这个变量,如果变量指示它要停止运行,则从它的run方法有序地返回。如果目标线程等待很长时间(例如,在条件变量上),则应使用中断方法来中断等待。可以看到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的理由:这个方法已经被弃用了,因为它天生就容易死锁。如果目标线程在挂起时锁定保护关键系统资源的监视器,则在目标线程恢复之前,任何线程都不能访问该资源。如果将恢复目标线程的线程在调用恢复之前尝试锁定此监视器,则会导致死锁。这种死锁通常表现为“冻结”进程。由此我们可以得出以下结论:suspend有天然的死锁倾向。当一个线程被挂起时,该线程持有的锁不会被释放,其他线程将无法访问这些资源。挂起一个线程后,如果在resume的过程中发生异常,resume方法执行失败,锁就无法释放,从而导致死锁。接下来模拟suspend导致的死锁场景。话不多说,展示我的代码:publicstaticvoidmain(String[]args)throwsInterruptedException{Objecto1=newObject();对象o2=新对象();Threadt1=newThread(()->{synchronized(o1){System.out.println("t1获得o1锁开始执行");try{Thread.sleep(5000);//模拟业务逻辑执行}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占用捏紧了,从此整个程序就陷入了无尽的等待----死锁参考:https://docs.oracle.com/javas...https://mp.weixin.qq.com/s/G_。..
