Java停止一个线程
1. 使用共享变量(标志位)
通过设置一个共享变量(标志位)来通知线程停止运行。
public class MyRunnable implements Runnable {
//定义一个变量作为是否允许运行的标志
private volatile boolean isAllowedToRun = true;
@Override
public void run() {
while (isAllowedToRun) {
// 执行一些任务
System.out.println("Thread is running...");
// 模拟一些工作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复中断状态
}
}
System.out.println("Thread is stopping...");
}
//停止线程的方法,原理是将"是否允许运行的变量"设为false, 是其线程不再循环
public void stopThread() {
isAllowedToRun = false;
}
public static void main(String[] args) throws InterruptedException {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
// 让主线程等待一段时间,然后停止子线程
Thread.sleep(5000);
myRunnable.stopThread();
}
}
2. 综合运用Thread的 interrupt()
, isInterrupted(),
interrupted`` , interrupted()
, interrupted()
, getAndClearInterrupt()
- thread实例.interrupt()
- thread实例.isInterrupted()
- thread 的volatile变量 interrupted , 与静态方法interrupted()同名
- Thread.interrupted() 就是调用 Thread.currentThread().getAndClearInterrupt() 完全等效
来自jdk21的源码集合, 调整了顺序,去掉原注释
//用于标志是否中断的变量, 与静态方法 interrupted() 同名interrupt0()原生native底层方法
volatile boolean interrupted;
// 用于中断线程, 会将interrupted变量设为true, 并调用
public void interrupt() {
if (this != Thread.currentThread()) {
checkAccess();
// thread may be blocked in an I/O operation
synchronized (interruptLock) {
Interruptible b = nioBlocker;
if (b != null) {
interrupted = true;
interrupt0(); // inform VM of interrupt
b.interrupt(this);
return;
}
}
}
interrupted = true;
interrupt0(); // inform VM of interrupt
}
private native void interrupt0();
public boolean isInterrupted() {
return interrupted;
}
public static boolean interrupted() {
return currentThread().getAndClearInterrupt();
}
boolean getAndClearInterrupt() {
boolean oldValue = interrupted;
if (oldValue) {
interrupted = false;
clearInterruptEvent();
}
return oldValue;
}
public final boolean isAlive() {
return alive();
}
boolean alive() {
return eetop != 0;
}
可以用
interrupt()
中断 配合Thread.interrupted()
检查是否中断interrupt()
中断 配合Thread.currentThread().isInterrupted()
检查是否中断
Thread.currentThread().isInterrupted()
与 Thread.interrupted()
的不同在于:
Thread.currentThread().isInterrupted()
返回 interrupted
属性的值, 其它什么也不做
public boolean isInterrupted() { return interrupted; }
Thread.interrupted()
是调用 Thread.currentThread().getAndClearInterrupt()
,
public static boolean interrupted() {
return currentThread().getAndClearInterrupt();
}
getAndClearInterrupt()
返回 interrupted
, 如果 interrupted==true
则 interrupted=false
将 interrupted
重设为false
boolean getAndClearInterrupt() {
boolean oldValue = interrupted;
// We may have been interrupted the moment after we read the field,
// so only clear the field if we saw that it was set and will return
// true; otherwise we could lose an interrupt.
if (oldValue) {
interrupted = false;
clearInterruptEvent();
}
return oldValue;
}
调用 Thread.interrupted()
之后, volatile成员变量interrupted
必为false
interrupt()
方法
通过调用线程的 interrupt()
方法来设置线程的中断状态,并在线程内部检查这个状态来决定是否退出。
public class MyRunnable implements Runnable {
@Override
public void run() {
// 循环判断条件可以用 !Thread.currentThread().isInterrupted() 或 Thread.interrupted()
while (!Thread.currentThread().isInterrupted()) {
// 执行一些任务
System.out.println("Thread is running...");
// 模拟一些工作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// 当线程被中断时,会抛出 InterruptedException,并清除中断状态
// 通常,我们会在这里重新设置中断状态,或者做一些清理工作
Thread.currentThread().interrupt(); // 重新设置中断状态,以便上层调用者知道中断发生了
}
}
System.out.println("Thread is stopping...");
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new MyRunnable());
thread.start();
// 让主线程等待一段时间,然后中断子线程
Thread.sleep(5000);
thread.interrupt();
}
}
在Java中,interrupt()
方法是用于中断线程的一种机制。当你调用一个线程的 interrupt()
方法时,该线程的中断状态将被设置为 true
。线程本身并不会立即停止执行,而是会在其运行过程中检查这个中断状态,并根据需要作出响应。
中断机制的工作原理
-
设置中断状态:当你调用
interrupt()
方法时,目标线程的中断状态被设置为true
。这个状态是一个布尔值,表示线程是否已经被中断。 -
检查中断状态:线程在其执行过程中应该定期检查这个中断状态。这通常是通过检查
Thread.interrupted()
(静态方法,会清除中断状态)或Thread.currentThread().isInterrupted()
(实例方法,不会清除中断状态)来实现的。 -
响应中断:一旦线程检测到它已被中断(即中断状态为
true
),它应该根据设计的逻辑来响应这个中断。这可能意味着立即退出循环、执行一些清理操作、抛出InterruptedException
(如果线程正在执行一个可中断的阻塞操作,如Thread.sleep()
、Object.wait()
或Selector.select()
),或者执行其他任何适当的操作。
注意事项
-
中断是一种协作机制:线程不会立即停止执行,而是会在适当的时候检查并响应中断。这意味着线程必须自己编写代码来检查中断状态并作出反应。
-
清除中断状态:调用
Thread.interrupted()
方法会返回当前线程的中断状态,并将该状态设置为false
。如果你希望保留中断状态以便稍后检查,应该使用Thread.currentThread().isInterrupted()
方法。 -
可中断的阻塞方法:一些Java库中的方法(如
Thread.sleep()
、Object.wait()
和Selector.select()
)是可中断的。当这些方法被调用时,如果线程被中断,它们会抛出InterruptedException
并清除中断状态。这意味着,如果你的线程在这些方法上阻塞,并且你希望它能够在被中断时立即响应,那么你应该捕获这个异常并适当地处理它。 -
不可中断的阻塞:有些操作(如I/O操作或某些类型的同步)可能不是可中断的。在这种情况下,你可能需要实现自己的机制来检查中断状态并响应中断,例如通过轮询中断状态或使用其他形式的线程间通信。
示例代码
以下是一个使用 interrupt()
方法来中断线程的示例:
public class InterruptableThread extends Thread {
@Override
public void run() {
try {
while (!Thread.currentThread().isInterrupted()) {
// 执行一些任务
System.out.println("Thread is running...");
// 模拟一些工作,这里使用sleep来模拟阻塞
Thread.sleep(1000);
// 可以在这里添加其他逻辑来检查中断状态并响应中断
}
} catch (InterruptedException e) {
// 如果线程在sleep时被中断,InterruptedException会被抛出
// 在这里处理中断,例如执行清理操作
System.out.println("Thread was interrupted during sleep. Cleaning up...");
// 通常,在捕获InterruptedException后,应该再次设置中断状态,
// 因为InterruptedException并不总是表示线程应该完全停止。
// 这取决于你的应用程序的逻辑。
// 在这个简单的例子中,我们不需要再次设置中断状态。
}
System.out.println("Thread is stopping...");
}
public static void main(String[] args) throws InterruptedException {
InterruptableThread thread = new InterruptableThread();
thread.start();
// 让主线程等待一段时间,然后中断子线程
Thread.sleep(5000);
thread.interrupt();
// 等待子线程完全停止(在实际应用中,你可能不需要这样做,
// 因为这可能会导致主线程阻塞。这里只是为了演示目的。)
thread.join();
}
}
在这个示例中,线程会每秒打印一次消息,直到它被中断。主线程在启动子线程后等待5秒,然后调用子线程的 interrupt()
方法来中断它。子线程在捕获到 InterruptedException
后执行清理操作并停止运行。
Thread.interrupted()
方法:
这个方法会检查当前线程是否被中断,并且会清除当前线程的中断状态。
Thread t = new Thread(new Runnable() {
public void run() {
// Thread.currentThread().getAndClearInterrupt() 可以等效换成 Thread.interrupted()
// Thread.interrupted() 源码就是调用 Thread.currentThread().getAndClearInterrupt() , 没有多余
while (!Thread.interrupted()) {
// 做一些工作
}
}
});
t.start();
// 在需要的时候停止线程
t.interrupt();
Thread.currentThread().getAndClearInterrupt()
可以等效换成 Thread.interrupted()
Thread.interrupted()
源码就是调用 Thread.currentThread().getAndClearInterrupt()
, 没有多余
public static boolean interrupted() {
return currentThread().getAndClearInterrupt();
}
getAndClearInterrupt()
返回 interrupted
, 如果 interrupted==true
则 interrupted=false
将 interrupted
重设为false
boolean getAndClearInterrupt() {
boolean oldValue = interrupted;
// We may have been interrupted the moment after we read the field,
// so only clear the field if we saw that it was set and will return
// true; otherwise we could lose an interrupt.
if (oldValue) {
interrupted = false;
clearInterruptEvent();
}
return oldValue;
}
isInterrupted()
返回 interrupted
其它什么也不做
public boolean isInterrupted() { return interrupted; }
3. 使用 Future
和 ExecutorService
对于需要更高级控制的情况,可以使用 ExecutorService
和 Future
来管理线程,并通过 Future.cancel()
方法来停止线程。不过,这通常适用于那些可以通过 Callable
任务提交给 ExecutorService
的情况。
import java.util.concurrent.*;
public class MyCallable implements Callable<Void> {
@Override
public Void call() throws Exception {
while (!Thread.currentThread().isInterrupted()) {
// 执行一些任务
System.out.println("Thread is running...");
// 模拟一些工作
Thread.sleep(1000);
}
System.out.println("Thread is stopping...");
return null;
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<Void> future = executorService.submit(new MyCallable());
// 让主线程等待一段时间,然后取消任务
Thread.sleep(5000);
future.cancel(true); // 尝试停止任务,如果正在运行则中断线程
// 关闭ExecutorService
executorService.shutdown();
}
}
5. 不推荐使用 Thread.stop()
方法, 已无效,什么也不干,只抛个异常
stop()
方法不但在1.2版就已经@Deprecated, 而且无效,什么也不干,只抛个异常
jdk21中的源码如下:
@Deprecated(since="1.2", forRemoval=true)
public final void stop() {
throw new UnsupportedOperationException();
}
在Java中,Thread.stop()
方法是一个已废弃(deprecated)的方法,用于强制终止线程的执行。然而,由于其潜在的危险性和不可预测性,这种方法在现代Java编程中通常是不被推荐的。
为什么 Thread.stop()
被废弃?
-
数据不一致:强制终止线程可能会导致它正在操作的数据处于不一致的状态。例如,如果线程在更新多个相关数据结构时被停止,那么这些数据可能会部分更新,从而导致数据损坏。
-
死锁风险:
Thread.stop()
方法会立即释放线程所持有的所有监视器锁。这可能会导致其他线程试图获取这些锁时发生死锁,因为释放锁的操作并不是在安全的上下文中进行的。 -
无法清理资源:被强制终止的线程可能没有机会执行任何清理代码,如关闭文件、释放数据库连接等。
-
不安全的操作:
Thread.stop()
方法可能会导致不安全的操作,因为它会立即停止线程,而不考虑线程当前正在执行的操作的性质。
替代方案
由于 Thread.stop()
的上述缺陷,Java推荐使用更安全和更可控的方式来停止线程:
-
使用共享变量(标志位):线程可以定期检查一个共享变量(通常是一个volatile修饰的布尔值),以确定是否应该停止执行。
-
使用
interrupt()
方法:通过调用线程的interrupt()
方法来设置线程的中断状态。线程内部应该定期检查这个状态,并在适当的时候响应中断,通常是通过抛出InterruptedException
或执行一些清理操作。 -
使用
ExecutorService
和Future
:对于需要更高级线程管理的场景,可以使用ExecutorService
来提交和管理任务。通过调用Future.cancel()
方法,可以请求取消任务的执行。如果任务正在运行,那么它会收到一个中断请求。
示例代码
以下是一个使用 interrupt()
方法来停止线程的示例:
public class SafeStopThread extends Thread {
@Override
public void run() {
try {
while (!Thread.currentThread().isInterrupted()) {
// 执行一些任务
System.out.println("Thread is running...");
// 模拟一些工作
Thread.sleep(1000);
}
} catch (InterruptedException e) {
// 线程被中断时,InterruptedException 被抛出
// 可以在这里执行一些清理操作
System.out.println("Thread was interrupted. Cleaning up...");
}
System.out.println("Thread is stopping...");
}
public static void main(String[] args) throws InterruptedException {
SafeStopThread thread = new SafeStopThread();
thread.start();
// 让主线程等待一段时间,然后中断子线程
Thread.sleep(5000);
thread.interrupt();
}
}
在这个示例中,线程会定期检查它的中断状态,并在被中断时执行清理操作,然后安全地停止。这种方法比使用 Thread.stop()
更加安全和可控。
注意事项
- 避免使用
Thread.stop()
:这是不推荐的方法,因为它会立即停止线程,并且不会释放任何监视器锁,可能会导致数据不一致和死锁。 - 优雅地处理中断:当线程被中断时,应该优雅地处理中断状态,确保资源的正确释放和任务的正确终止。
通过以上方法,你可以更安全和优雅地停止一个Java线程。