为什么不在当前线程中实现一个时间控制的任务呢?

Why not implement a time controlled task in the current thread?

当实现在特定时间范围内处理任务的功能时(例如,每 1 分钟监视一个值,总共 10 分钟的时间限制),流行的解决方案是使用 TimerScheduledExecutorService java util 类 在单独的线程上安排任务。

想知道为什么不直接在当前线程上实现定时任务,而是创建一个新线程来处理它?

您不能在当前线程中执行此操作,因为您的主程序 运行 在它自己的线程中,您无法对其进行太多控制。你最多可以让它进入睡眠状态。因此,您将在一个线程上启动您的主程序,该线程将分拆另一个线程以 运行 按计划执行您的任务。

休眠主线程会冻结您的应用程序

multi-threading 的全部要点是从 运行 应用程序的初始线程卸载工作。

一个线程休眠到某个时间没有做任何其他工作。如果您的应用程序的主线程处于休眠状态,那么您的应用程序将完全挂起且无用。如果线程 运行使您的 GUI 处于休眠状态,那么您的应用程序对用户来说似乎是冻结的,显然是崩溃的,并且实际上毫无用处。

另外,作为, threads as currently implemented in Java are relatively expensive in terms of memory and CPU. So we generally avoid having more than a few/several/dozens of threads running at a time. In the future, if Project Loom succeeds with its virtual threads,这种情况可能会发生根本性的改变。

您发表评论:

I think maybe I can do start time stop->monitor-> return-> sleep current thread-> monitor, quit monitor when condition met or time out?

这实际上就是 ScheduledExecutorService 所做的。您将任务定义为 RunnableCallable。执行程序服务 运行 定期执行您的任务。在该任务中,您可以检查您正在等待的条件。

所以你不需要重新发明轮子。使用内置于 Java 中的 Executors 框架;太棒了

搜索那个 class 名字。关于这个主题已经写了很多,其中一些是我撰写的。搜索以了解更多信息。

关于 Timer,class 多年前已被 Executors 框架取代。 Java文档中记录了这一事实。

任务自行重新安排

您阐明了您的目标是每分钟检查一次条件,最多十次。

执行此操作的一种方法是让任务自行重新安排。

public class ConditionCheckingTask implements Runnable 
{
    private final ScheduledExecutorService ses ;
    private final Instant whenInstantiated = Instant.now() ;

    // Constructor
    public ConditionCheckingTask( final ScheduledExecutorService ses ) {
        this.ses = ses ;
    }

    @Override
    public void run() {
        if( someConditionIsTrue ) {
            doSomething ;
        } else if ( ChronoUnit.MINUTES.between( this.whenInstantiated , Instant.now() ) > 10 ) {
            // We have exceeded our time limit, so let this task die.
            return ;
        } else {  // Else wait a minute to check condition again.
            this.ses.schedule( this , 1 , TimeUnit.MINUTES ) ;
        }
    }
}

确保访问 someConditionIsTrue 是 thread-safe。例如,如果某个其他线程正在翻转一个布尔标志来指示我们的任务,则将该标志设置为 AtomicBoolean。要了解更多信息,请搜索 Stack Overflow 并阅读下面列出的书籍。

在您的应用中的某处,实例化并记住一个 ScheduledExecutorService 对象。

ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor() ;

确保在您的应用程序退出之前最终关闭执行程序服务。搜索以了解更多信息。这已在 Stack Overflow 上多次解决。

实例化你的任务。将计划的执行程序服务传递给任务的构造函数,以便它可以每分钟重新安排一次。

ConditionCheckingTask task = new ConditionCheckingTask( scheduledExecutorService ) ;

要开始工作,请安排 运行 该任务,几乎没有延迟。

scheduledExecutorService.schedule( task , 0 , TimeUnit.MINUTES ) ;

作为第一个 运行 的一部分,任务将自行重新安排。


注意:在部署任何 multi-threaded 代码之前,请务必仔细阅读 Goetz 等人的优秀书籍,Java Concurrency In Practice

Wondering why not just implement the timed task on the current thread instead of creating a new thread to handle that?

您的“当前”线程可能是正在处理主要请求的线程(例如,http 请求或 task/job 协调器)。其中之一的定时任务会将此线程与任何其他工作联系起来,直到任务完成。在基于 http 的服务上,这可能会导致 http 超时等,这对于最终用户体验和计算资源使用来说都不是一个好的设计。 通常,作为一种好的做法,您不会为该任务生成另一个新线程,而是使用线程池来有效利用资源。