上一篇文章讲到了interrupt()、interrupted()、isInterrupted()这三个方法,最后留了个尾巴,那么今天这一篇文章就给大家讲一下怎样停止线程。
stop()方法
说到线程停止,第一个想到的办法就是Thread类的stop()方法,下面就先讲一下stop()方法。
@Deprecated
public final void stop() {
...
}
stop()方法可以暴力的停止一个线程,直接打断线程的运行。
但是我们可以观察到stop()方法被@Deprecated修饰,标明是一个废弃的方法,不建议使用,那么下面我们就来说一下使用stop()方法可能带来的一些问题。
1.调用stop()方法会抛出java.lang.ThreadDeath错误
public class StopTest {
public static void main(String[] args) {
TestStopThread t1 = new TestStopThread();
t1.start();
}
}
class TestStopThread extends Thread {
@Override
public void run() {
try {
this.stop();
} catch (ThreadDeath e) {
System.out.println("抛出ThreadDeath异常" + e.toString());
e.printStackTrace();
}
}
}
2.破坏锁,导致数据不一致。
大家都知道用synchronized关键字修饰一个方法的时候,可以保证这个方法是原子性的,而stop()方法被调用时会直接释放锁,可能会导致数据不一致。
大家可以看看下面这个案例:
public class StopLockTest {
public static void main(String[] args) {
StopLockThread stopLockThread = new StopLockThread();
Thread t1 = new Thread(stopLockThread);
t1.start();
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
// t1.stop();
Thread t2 = new Thread(stopLockThread);
t2.start();
}
}
class StopLockThread extends Thread {
@Override
public void run() {
testSync();
}
public synchronized void testSync() {
System.out.println("start sync");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end sync");
}
}
执行上面这一段代码控制台输出
t1:start sync1532259460591
t1:end sync1532259461591
t2:start sync1532259461591
t2:end sync1532259462593
我们可以得到结论,t1、t2两个线程会同步的进入testSync()方法
而如果我们将t1.stop();的注释去掉,执行上面这一段代码控制台将输出
t1:start sync1532259482124
t2:start sync1532259482145
t2:end sync1532259483146
由于第一行输出和第二行输出时间戳只相差21毫秒,所以我们可以得出结论,t1.stop()直接打断了t1线程的运行,并且直接释放了testSync()方法的锁。
既然stop()方法存在这些问题,并且已经在JDK中被标注为废弃方法,所以强烈不建议在程序中使用stop()方法。
使用interrupt()方法停止线程
下面开始讲解上一篇文章留下的小尾巴了。
上一篇文章明确说明了interrupt()方法不会停止线程,只是会修改线程的interrupted状态,那么我们可以在线程中通过isInterrupted()方法来获取到线程的interrupted状态,然后通过异常或者return的方法来中止线程的运行。
public class ErrorStopTest {
public static void main(String[] args) {
ErrorStopThread t1 = new ErrorStopThread();
t1.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
t1.interrupt();
}
}
class ErrorStopThread extends Thread {
@Override
public void run() {
try {
while (true) {
if (this.isInterrupted()) {
System.out.println("线程interrupted状态为true,抛出异常中止线程");
throw new InterruptedException();
}
System.out.println(System.currentTimeMillis());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
上面这一段代码就是使用interrupt()方法和抛异常相结合的实现中止线程的方法,当然了使用return方法的原理也一致,主要步骤就是:
- 子线程中使用isInterrupted()方法来判断线程的interrupted状态
- 如果子线程的interrupted状态为true,那么结束线程执行的方法
- 主线程可以使用线程的interrupt()来修改子线程的interrupted状态
使用interrupt()方法停止线程相对于stop()方法要安全许多,所以强烈建议使用interrupt()方法来实现线程的停止。
题外话
线程停止的东西到这里就说完了,但是大家可能会发现上面代码中所有线程的sleep()方法都使用try-catch抓取了一个InterruptedException异常。
那么就说明sleep()方法会抛出InterruptedException异常,这里就再提一句
- 在线程interrupted状态为true时调用sleep方法时会抛出InterruptedException异常
@Test
public void interruptSleep(){
Thread.currentThread().interrupt();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
System.out.println("进入InterruptedException异常了");
e.printStackTrace();
}
}
- 在线程处于sleep状态的时候调用interrupt()方法会抛出InterruptedException异常
public class SleepInterruptTest {
public static void main(String[] args) {
SleepInterruptThread t1 = new SleepInterruptThread();
t1.start();
t1.interrupt();
}
}
class SleepInterruptThread extends Thread {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("进入InterruptedException异常了");
e.printStackTrace();
}
}
}
上面两段测试代码都会被捕获到InterruptedException异常,打印”进入InterruptedException异常了”这句话
喜欢文章的,扫码关注微信公众号