此问题已在此处有答案:
How to timeout a thread(17回答)
java killing a thread after timeout value [duplicate](5个答案)
stop a thread in java after a given time - doesn't work(5个答案)
How to stop execution after a certain time in Java?(4个答案)
昨天关门了。
我有一个Job类,它在执行器服务中触发3个不同的可运行任务。线程可能需要更长的时间(有时甚至需要1小时才能完成任务)。如果它们花费的时间比预期的要长,我想停止线程。有没有一种有效的方法来停止异步进程?
作业在某个时间点被触发(使用调度程序cron exp),如果作业占用更多时间,则需要停止作业。
public class MyJob {
. . . . . . . . . .
. . . . . . . . . .
public void process(..) {
. . . . . . . . . .
. . . . . . . . . .
Runnable r1 = () -> { task1() };
Runnable r2 = () -> { task2() };
Runnable r3 = () -> { task3() };
List.add(r1, r2, r3);
}
private void task1() {
. . . . . . . . . .
}
private void task2() {
. . . . . . . . . .
}
private void task3() {
. . . . . . . . . .
}
private void runAsync(List<Runnable> tasks) {
. . . . . . . . . .
myExecutorService.submitAll(tasks);
. . . . . . . . . .
}
}
我读到过中断,但不确定它是否符合我的要求。我想定义一个API端点,通过它我可以停止异步进程。
3条答案
按热度按时间w1jd8yoj1#
在Java中,基本上有4个原语来支持线程间通信:
1.不稳定的变量。
1.中断系统。
1.同步(方法或块)。
为了完整起见,还有
Thread.stop()
和Thread.suspend()
方法-但根据文档,它们本质上是不安全的,不应该使用。现在已弃用,最终将被删除。参见Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?。此外,你应该查看
java.util.concurrent
包--然而我的理解是,并发包是建立在上面列出的4个原语之上的,因此它不提供任何额外的功能(你不能使用这些原语自己实现)。上面列出的前两个原语可以被视为暂停/中止运行线程的机制。最后两个与同步/正确性和性能优化(没有空闲线程燃烧CPU)更相关。
volatile变量最简单的情况是一个布尔标志
shouldTerminate
,它被初始化为false。那么你在工作线程中的主处理循环通常会有如下内容:因此,如果你想用信号通知一个工作线程终止,你可以简单地设置
shouldTerminate = true;
,工作线程就会跳出它的循环。您有多少个标志以及哪些worker线程使用哪些标志决定了您对worker何时终止的控制级别。(仅)使用volatile变量的核心问题是线程可能会以多种方式被阻塞:
如果你只使用volatile变量,线程不会中止,直到任何情况下阻止它的执行被清除-一旦块被清除,线程将能够检查变量,如果需要关闭。
这就是notify()和interrupt()的作用:
注意:
notify()
唤醒一个(单个)随机线程,因此,如果你有多个线程在一个监视器上等待,通常使用notifyAll()
是一个好的做法。有没有一种有效的方法来停止异步进程?
在调度作业时,您可以在返回的
Future
上执行cancel()
,但如果作业已经在运行,则将其实现为interrupt()
。或者,您可以定义一个变量来向作业发出它应该终止的信号--这通常是一个更干净的解决方案--但它有上面概述的缺点。
编辑-原子类
正如@Basil Bourque -在评论中指出的那样,
Atomic...
类是一个灰色地带。我不认为它们本身是原语,但它们确实利用了我在上面的列表中没有提到的原语(来自VarHandle
类的CAS操作)。上面的原语列表通常是您可以选择直接使用的,我建议使用
Atomic...
类,而不是直接调用CAS操作。TBF -自从添加了
java.util.concurrent
之后,我就没有使用过interrupt()
或等待/通知太多。我通常会发现其中一个并发类满足我的需求,但我仍然使用volatile布尔值来终止后台线程的执行--这是因为我不知道第三方库(通常用于工作线程)如何科普中断。因此,我宁愿接受会有延迟,而任何IO在终止发生之前完成。nfs0ujit2#
这取决于你是否想控制止损。
当你将
submit()
一个Runnable
放入执行器服务时,你会得到一个Future
,它声明了一个方法cancel(booleam mayInterruptIfRunning)
。您可以:
要么你公开这个期货列表,让调用者决定该怎么做。在这种情况下,你的
void process()
变成了List<Future<?>> process()
,所以调用这个方法的人,拥有正在运行的任务列表,并可以决定停止其中的一些任务。或者,您在内部Map这些任务并公开一个方法来停止任务,例如,通过名称。就像这样:
他们可以在外面给你打电话
6qftjkof3#
我个人会将任务的开始日期保存到一个全局变量中,并创建另一个调度程序,它会在指定的时间间隔(取决于您的需要)进行检查,如果之前存储的现在开始日期高于1小时。如果是,我会调用myExecutorService。shutdownNow()方法。