我有一个 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 端点,通过它我将停止异步进程。
在 Java 中,基本上有 4 个原语支持线程间通信:
Thread.stop()
和 Thread.suspend()
方法 - 但根据文档,它们本质上是不安全的,不应使用。现在已弃用以最终删除。请参阅为什么不推荐使用 Thread.stop、Thread.suspend 和 Thread.resume?.
java.util.concurrent
包 - 但是我的理解是并发包构建在上面列出的 4 个原语之上,因此它不提供任何额外的功能(您无法自己实现使用那些原语)。
上面列出的前两个原语可以看作是暂停/中止正在运行的线程的机制。最后两个与同步/正确性和性能优化(没有空闲线程燃烧 CPU)更相关。
volatile 变量最简单的情况是一个布尔标志
shouldTerminate
它被初始化为 false。那么你在工作线程中的主处理循环通常会有类似的东西:
while(!shouldTerminate) {
// Do something.
}
因此,如果您想向工作线程发出终止信号,您只需设置
shouldTerminate = true;
,工作线程就会跳出循环。您拥有多少标志以及哪些工作线程使用哪些标志决定了您对何时终止工作人员的控制级别。
(仅)使用 volatile 变量的核心问题是线程可以通过多种方式被阻塞:
如果您只使用 volatile 变量,线程将不会中止,直到清除阻止其执行的任何情况 - 一旦清除块,线程将能够检查变量,并在需要时关闭。
这就是 notify() 和 interrupt() 发挥作用的地方:
注意:
notify()
唤醒(单个)random 线程因此,如果您有多个线程在单个监视器上等待,通常最好使用 notifyAll()
.
有没有一种有效的方法来停止异步进程?
你可以
cancel()
在 Future
上你在安排作业时回来 - 但如果作业已经在运行,这将作为 interrupt()
.
或者,您可以定义一个变量来向它应该终止的作业发出信号——这通常是一个更简洁的解决方案——但它具有上述缺点。
就看你要不要控制止损了
当你
submit()
一个 Runnable
进入执行者服务时,你会得到一个 Future
声明一个方法 cancel(booleam mayInterruptIfRunning)
.
由您决定:
要么公开这个期货列表,让调用者决定做什么。在这种情况下,您的
void process()
变成了 List<Future<?>> process()
所以谁调用这个方法,有正在运行的任务列表,并且可以决定停止其中的一些。
或者,您在内部映射这些任务并公开一个方法来停止任务,例如,按名称。像这样的东西:
Map<String, Future<?>> tasksMap = new HashMap<>();
tasksMap.add("task1", executorService.submit(runnable1));
tasksMap.add("task2", executorService.submit(runnable2));
//etc.
他们可以这样从外面给你打电话:
public void stopTask(String taskName) {
tasksMap.get(taskName).cancel(true/false);
//I let you design the proper checks to make sure an entry of the map is present and that the thread is still running before cancelling it
}
我会亲自将任务的开始日期保存到一个全局变量中,并创建另一个调度程序,如果较早存储的现在开始日期高于 1 小时,则以指定的时间间隔(取决于您的需要)进行检查。 如果是,我会调用 myExecutorService.shutdownNow() 方法。