在Java中没有正式的方法来杀死一个线程。停止一个线程完全由JVM管理。尽管Java提供了几种管理线程生命周期的方法,如start()、sleep()、stop()(在Java 1 .1中被废弃 )等,但没有提供任何方法来杀死一个线程并干净地释放资源。
甲骨文公司指出,废弃stop()方法的原因是它本身就不安全。停止一个线程会导致它解锁所有锁定的监视器。
1.杀死一个线程的两种方法
有效地,我们只能向线程发出信号,让它停止自己,让线程清理资源并终止自己。在Java中,我们可以通过两种方式向线程发送信号。
- 通过周期性地检查一个布尔标志
- 通过使用Thread.interrupt()方法来中断线程
让我们了解一下这两种方法。
2.通过检查一个标志
在这个方法中,我们定期检查一个布尔标志,或者在任务的每一步之后。最初,该标志被设置为false。要停止线程,就把标志设为*"真*"。在线程内部,当代码将标志的值检查为真时,它会优雅地销毁自己并返回。
请注意,在这种设计中,一般来说,有两个线程。一个线程将标志值设置为真,另一个线程检查标志值。为了确保两个线程一直看到相同的值,我们必须让标志变量 挥发性.或者我们可以使用AtomicBoolean类,它支持对底层的 易失性 布尔变量进行原子操作。
public class CustomTask implements Runnable {
private volatile boolean flag = false;
private Thread worker;
public void start() {
worker = new Thread(this);
worker.start();
}
public void stop() {
flag = true;
}
@Override
public void run() {
while (!flag) {
try {
Thread.sleep(500);
System.out.println(Thread.currentThread().getName() + " Running...");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("Thread was interrupted," + e.getMessage());
}
}
System.out.println(Thread.currentThread().getName() + " Stopped");
return;
}
}
让我们通过创建两个线程并停止它们来测试这个设计。
CustomTask task1 = new CustomTask();
CustomTask task2 = new CustomTask();
task1.start();
task2.start();
try {
Thread.sleep(1000);
task1.stop();
task2.stop();
} catch (InterruptedException e) {
System.out.println("Caught:" + e);
}
Thread-0 Running...
Thread-1 Running...
Thread-1 Running...
Thread-0 Running...
Thread-1 Stopped
Thread-0 Stopped
3.通过中断线程
这个方法也与之前使用标志的方法非常相似。唯一的区别是,我们将中断线程,而不是将标志设置为false。
所以在线程内部,我们将不断检查线程的中断状态,当线程被中断时,我们将优雅地停止线程。为了检查被中断的线程的状态,我们可以使用 Thread.isInterrupted()方法。它根据线程的中断状态返回真或假。
public class CustomTaskV2 implements Runnable {
private Thread worker;
public void start() {
worker = new Thread(this);
worker.start();
}
public void interrupt() {
worker.interrupt();
}
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(500);
System.out.println(Thread.currentThread().getName() + " Running...");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("Thread was interrupted with reason : " + e.getMessage());
}
}
System.out.println(Thread.currentThread().getName() + " Stopped");
return;
}
}
有趣的是,测试这一设计的代码与之前的设计类似。只有对thread.interrupt()的方法调用是额外的。
CustomTaskV2 task1 = new CustomTaskV2();
CustomTaskV2 task2 = new CustomTaskV2();
task1.start();
task2.start();
try {
Thread.sleep(1100);isInterrupted
task1.interrupt();
task2.interrupt();
} catch (InterruptedException e) {
System.out.println("Caught:" + e);
}
Thread-0 Running...
Thread-1 Running...
Thread-1 Running...
Thread-0 Running...
Thread was interrupted with reason : sleep interrupted
Thread was interrupted with reason : sleep interrupted
Thread-0 Stopped
Thread-1 Stopped
4.总结
本教程教会了我们使用自定义的解决方案来杀死Java中正在运行的线程。尽管布尔标志的解决方案非常好,但对于线程大部分时间都在等待的长时间运行的任务来说,它可能无法得到理想的结果。
发送一个中断是一个更好的方法来停止一个长期等待的线程。