咱们晓得像 stop、suspend 这几种中断或者阻塞线程的办法在较高 java 版本中曾经被标记上了 @Deprecated 过期标签,那么为什么她们已经登上了 java 的历史舞台而又慢慢的推出了舞台呢,到底是兽性的扭曲还是道德的沦丧呢,亦或是她们不思进取被取而代之呢,如果是被取而代之,那么取而代之的又是何方人也,本文咱们将一探到底。
一、stop 的闭幕
首先 stop 办法的作用是什么呢,用 java 源码中的一句正文来理解一下:Forces the thread to stop executing.,即强制线程进行执行,’Forces’仿佛曾经透漏出了 stop 办法的蛮狠无理。那么咱们再看看 java 开发者是怎们解释 stop 被淘汰了的:
咱们从中能够看出以下几点:
1.stop 这种办法实质上是不平安的
2. 应用 Thread.stop 进行线程会导致它解锁所有已锁定的监视器,即间接开释以后线程曾经获取到的所有锁,使得以后线程间接进入阻塞状态
咱们举例来看一下上边提到的两点:
public static void main(String[] args) throws InterruptedException {Object o1=new Object();
Object o2=new Object();
Thread t1=new Thread(()->{synchronized (o1)
{synchronized (o2)
{
try {System.out.println("t1 获取到锁");
Thread.sleep(5000);
System.out.println("t1 完结");
} catch (InterruptedException e) {e.printStackTrace();
}
}
}
});
t1.start();
Thread.sleep(1000);
Thread t2=new Thread(()->{synchronized (o1)
{synchronized (o2)
{
try {System.out.println("t2 获取到锁");
Thread.sleep(5000);
System.out.println("t2 完结");
} catch (InterruptedException e) {e.printStackTrace();
}
}
}
});
t2.start();
t1.stop();}
运行后果:
能够看到,当线程 t1 在获取到 o1 和 o2 两个锁开始执行,在还没有执行完结的时候,主线程调用了 t1 的 stop 办法中断了 t1 的执行,开释了 t1 线程获取到的所有锁,中断后 t2 获取到了 o1 和 o2 锁,开始执行直到完结,而 t1 却夭折在了 sleep 的时候,sleep 后的代码没有执行。
因而应用 stop 咱们在不晓得线程到底运行到了什么中央,暴力的中断了线程,如果 sleep 后的代码是资源开释、重要业务逻辑等比拟重要的代码的话,亦或是其余线程依赖 t1 线程的运行后果,那间接中断将可能造成很重大的结果。
那么不倡议应用 stop 中断线程咱们应该怎么去优雅的完结一个线程呢,咱们能够存 java 开发者的正文中窥探到一种解决方案:
能够看到 java 开发者举荐咱们应用以下两种办法来优雅的进行线程:
1. 定义一个变量,由指标线程去一直的查看变量的状态,当变量达到某个状态时进行线程。
代码举例如下:
volatile static boolean flag=false;
public static void main(String[] args) throws InterruptedException {Object o1=new Object();
Thread t1=new Thread(()->{synchronized (o1)
{
try {System.out.println("t1 获取到锁");
while (!flag)
Thread.sleep(5000);// 执行业务逻辑
System.out.println("t1 完结");
} catch (InterruptedException e) {e.printStackTrace();
}
}
});
t1.start();
Thread.sleep(1000);
Thread t2=new Thread(()->{synchronized (o1)
{
try {System.out.println("t2 获取到锁");
Thread.sleep(5000);// 执行业务逻辑
System.out.println("t2 完结");
} catch (InterruptedException e) {e.printStackTrace();
}
}
});
t2.start();
flag=true;
}
运行后果:
2. 应用 interrupt 办法中断线程。
代码举例如下:
public static void main(String[] args) throws InterruptedException {Object o1=new Object();
Thread t1=new Thread(()->{synchronized (o1)
{System.out.println("t1 获取到锁");
while (!Thread.currentThread().isInterrupted()) {for (int i = 0; i < 100; i++) {if(i==50)
System.out.println();
System.out.print(i+" ");
}
System.out.println();}
System.out.println("t1 完结");
}
});
t1.start();
Thread t2=new Thread(()->{synchronized (o1)
{
try {System.out.println("t2 获取到锁");
Thread.sleep(5000);// 执行业务逻辑
System.out.println("t2 完结");
} catch (InterruptedException e) {e.printStackTrace();
}
}
});
t2.start();
t1.interrupt();}
运行后果:
咱们用 while (!Thread.currentThread().isInterrupted()) 来一直判断以后线程是否被中断,中断的话则让线程天然沦亡并开释锁。能够看到调用 interrupt 办法后并不会像 stop 那样暴力的中断线程,会等到以后运行的逻辑完结后再查看是否中断,十分的优雅。
注:运行举例代码可能不会打印出数字,这是因为 t1 线程运行到 while(!Thread.currentThread().isInterrupted()) 时,主线程曾经调了 interrupt 办法,因而屡次运行可能会打印出数字。
二、suspend 的闭幕
suspend 办法的作用是挂起某个线程直到调用 resume 办法来复原该线程,然而调用了 suspend 办法后并不会开释被挂起线程获取到的锁,正因如此就给 suspend 和 resume 这哥俩贴上了容易引发死锁的标签,当然这也正是导致 suspend 和 resume 退出历史舞台的罪魁祸首。同样咱们看看 java 开发者为 suspend 的淘汰给出的理由:
从中咱们能够得出以下论断:
1.suspend 具备人造的死锁偏向
2. 当某个线程被 suspend 后,该线程持有的锁不会被开释,其余线程也就不能拜访这些资源
3.suspend 某个线程后,如果在 resume 的过程中出现异常导致 resume 办法执行失败,则 lock 无奈开释,导致死锁
接下来模仿一下由 suspend 引起的死锁场景,Talk is cheap,show my code:
public static void main(String[] args) throws InterruptedException {Object o1=new Object();
Object o2=new Object();
Thread t1=new Thread(()->{synchronized (o1)
{System.out.println("t1 获取到 o1 锁开始执行");
try {Thread.sleep(5000);// 模仿执行业务逻辑
} catch (InterruptedException e) {e.printStackTrace();
}
System.out.println("t1 执行完结");
}
});
t1.start();
Thread t2=new Thread(()->{synchronized (o2)
{System.out.println("t2 获取到 o2 开始执行");
try {Thread.sleep(2000);// 执行耗时业务
} catch (InterruptedException e) {e.printStackTrace();
}
synchronized (o1)
{System.out.println("t2 获取到 o1 锁开始继续执行");
}
System.out.println("t2 执行完结");
}
});
t2.start();
Thread.sleep(1000);
t1.suspend();
// 假如抛出了一个未知异样
int i=1/0;
t1.resume();}
运行后果:
能够看到,整个程序卡的死死的,在调用 resume 复原 t1 线程之前抛出了一个未知异样,导致 t1 始终挂起进而无奈开释 o1 锁,而 t2 须要获取到 o1 锁后能力继续执行,但苦苦期待,奈何 o1 被 t1 拿捏的死死的,从此整个程序就陷入了无尽的期待中 —- 死锁。
作者:浪舟子
blog.csdn.net/qq_40400960/article/details/112651249