我想立即停止一个Java线程,但每次尝试停止线程时,它总是需要一些时间才能停止。是否有一种好的方法可以强制Java线程立即停止?
我想立即停止一个Java线程,但每次尝试停止线程时,它总是需要一些时间才能停止。是否有一种好的方法可以强制Java线程立即停止?
没有一种好的方法可以立即停止一个线程。
有Thread.stop()
,但它是危险的和已弃用。除非:
您完全了解使其危险的问题,
您已经彻底分析了您的代码并确定了问题不适用和/或风险可接受,
您不关心应用程序在方法被删除时可能会停留在某个Java版本上1。
曾经有Thread.stop(Throwable)
,但同样很危险2,已弃用,在Java 11中已被删除3。
有Thread.interrupt()
,但不能保证线程会快速停止,甚至根本不会停止。行为将取决于应用程序线程代码是否已设计为注意并正确响应中断。 (请参见下文。)
有一种方法是编写线程以定期检查标志;请参阅此答案。 (请参见下文。)
interrupt()
的方法基本相同。在两种情况下,期望被中断的线程需要定期检查;例如通过调用interrupted()
或isInterrupted()
或通过检查标志。interrupt()
机制将在代码等待notify()
, join()
等或在I/O操作中阻塞时起作用。由于这个原因,并且因为interrupt
是一种被广泛接受的应用程序无关的方式,通常优先实现特定于应用程序的flag
机制。
1 - 在Java 18预发布的javadocs中,Thread.stop()
现在被标记为“即将删除”。
2 - 可能更甚。例如,如果您执行了Thread.stop(new IOException())
,一些调用堆栈上方的代码可能会轻松捕获异常并破坏您的“停止”功能。
3 - 您仍然可以通过反射调用私有的Thread.stop0(Throwable)
方法来使用旧的“带有异常的停止”功能;请参见https://dev59.com/JG435IYBdhLWcg3wvyw5#67077495。最好避免这样做。
InterruptedIOException
异常。(如果无法中断在I/O中阻塞的线程,那将是非常糟糕的...) - Stephen C有一种安全(而且快速)停止线程的好方法:
System.exit(0)
不幸的是,这会产生副作用,导致所有其他线程停止运行。
运行中的线程无法使用 Thread.Interrupt
停止,只有等待或阻塞的线程可以使用 Thread.Interrupt
停止。但可以使用共享变量来发出应该停止正在执行的信号。线程应该定期检查变量(例如:使用 while 循环),并以有序的方式退出。
private boolean isExit= false;
public void beforeExit() {
isExit= true;
}
public void run() {
while (!isExit) {
}
}
isExit
布尔值的变化应该受到某种同步保护,以确保变化的可见性。例如,可以将其标记为 volatile
。 - GPIstop(Throwable)
,因此需要使用反射,但底层本机方法stop0(Throwable)
(也由stop()
调用)仍然存在,尽管不可见。public static boolean stopWith(Thread t, Throwable e){
try {
Method m = Thread.class.getDeclaredMethod("stop0", Object.class);
m.setAccessible(true);
m.invoke(t, e);
m.setAccessible(false);
return true;
} catch (Exception e1) {
e1.printStackTrace();
}
return false;
}
这个方法适用于JDK 11,但是它代表着非法反射访问。
如果你的代码使用了模块化,那么默认情况下你的模块不能通过反射访问非公共成员(因为java.base/java.lang
对其他模块不是开放的)。因此会抛出一个java.lang.reflect.InaccessibleObjectException
异常。
可以使用JVM的--add-opens
标志覆盖默认开放java.base/
模块,例如:--add-opens java.base/java.lang=
your.module
。
如果你的代码没有使用模块化(即Java 8或之前的版本),由于为了向后兼容性而java.base/
对所有未命名模块(ALL-UNNAMED
)都是开放的,因此不会抛出任何异常。
但是会记录一条不可避免的警告信息:
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by YourCalss (file:/path/to/your/class/) to method java.lang.Thread.stop0(java.lang.Object)
WARNING: Please consider reporting this to the maintainers of TestSop
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
Thread.stop()
在Java 11中不是“隐形”的。 Thread.stop()
仍然是public
,仍然已过时。请查看Java 11 javadocs。2)在Java 11中,实际删除的是Thread.stop(Throwable)
(而不只是“隐形”)。3)您所调用的是一个私有本地方法...stop0(Throwable)
。这恰好是Thread.stop()
调用的方法。 - Stephen CThread.stop()
,则不必诉诸使用令人讨厌的、非可移植的、破坏抽象层的反射来实现。只有在需要使用特定于ThreadDeath
之外的特定异常时,才需要使用stop0(Throwable)
。(如果这样做,您需要考虑到某些内容可能会捕获您的异常...从而阻止“停止”。据我所知,这就是他们为什么删除了stop(Throwable)
的原因。) - Stephen Cstop(Throwable)
,你可以避免因错误而阻塞 stop()
。但是你仍然可以通过捕获 ThreadDeath
或 Throwable
来有意地阻塞。顺便说一下:已经在帖子中修正了错误的细节。 - Marco Torchiano你的线程可以放在一个 while 循环中,并每次检查一个布尔变量。当你将变量设置为 false 时,循环将结束,线程也将结束。要在通过当前循环之前结束线程,可以使用 break 关键字。
这避免了使用已弃用的方法,如 Thread.stop()
。
private volatile boolean run = true; // Needs be volatile ... or changes may
// not be visible to all threads.
//Call this method to end your thread
void stopRunning(){
run = false;
}
//This loop would go into the overided run() method
while(run){
//Put your task here
}
线程将完成当前任务,然后自然停止。
如果您需要在线程完成任务之前停止它,您可以在循环中使用 if/else 检查运行变量,并像其他任何地方一样结束线程。
while(run){
//Your code here
/*Be sure your not stopping the task at a point that will harm your program.*/
if(!run){break;}
}