为线程编写舒适的 pause/stop 方法

Writing a comfortable pause/stop method for a Thread

对于 Java 中的 Thread 子类,我试图编写一个方法 checkPauseAndStop(),其目的是成为一个很好的单行代码,我可以在 run() 方法中定期调用它检查是否有 pause/stop 请求并采取相应措施。

虽然功能齐全,但停止线程是通过 InterruptedException 处理的,这会强制 运行()-代码处于 try-catch 中并且感觉容易出错
(例如 Thread.sleep() 不会再告诉你它可能会抛出这样的异常)。

是否有任何好的方法可以在不妨碍 运行() 方法的情况下在方法内部处理停止线程?

代码示例:

public class SuspendableThread extends Thread
{
   private Semaphore lock = new Semaphore(1);
   public void checkPauseRequest() throws InterruptedException
    {
        if (isInterrupted())
            throw new InterruptedException();

        lock.acquire();
        lock.release();

    }

    @Override
    public void run()
    {
        try
        {
            while (true)
            {
                // Do_stuff
                checkPauseRequest();
            }

        }
        catch (InterruptedException e)
        {
            return;
        }
    }

当然,在这个特定示例中,根本没有必要。为了获得更大的效果,假设我们有 50 个或更多连续的

// Do_stuff
checkPauseRequest();

而不是只有一个。

如果你想捕获发送到线程的中断,例如执行清理,你不能绕过将整个 运行() 代码块放在 try/catch 异常块中.原因是如果你的线程正在执行任何类型的线程间信号,通过条件变量或信号量,例如(调用 wait() 方法),你的 运行() 代码块将立即抛出InterruptedException,如果为线程设置了中断状态标志。但是,如果您想主动检查线程的中断状态,最简单的方法是喷射一堆中断点,也就是您的 checkPause() 方法,然后休眠几纳秒(您的sleep() 如果设置了中断标志,方法将抛出 InterruptedException)。这可以在不影响线程性能的情况下实现您的目标。

请确保您要实现的目标真正有意义并且不属于让我们重新发明轮子类别。制定任何通用的通用停止方法都不是一件小事,尽管 Java 创作者看起来如此。这就是 Thread.stop() 方法被弃用的原因。 (https://docs.oracle.com/javase/10/docs/api/java/lang/Thread.html#stop())(并且 Thread.destroy() 实际上并未实施 AFAICT。)

有一个很好的解释:https://docs.oracle.com/javase/tutorial/essential/concurrency/interrupt.html

我会使用 ScheduledExecutorService(您可以使用 Executors.newSingleThreadScheduledExecutor 创建)和 enum RunningState { RUNNING, PAUSED, STOPPED }.

来构建它

当状态为RUNNING时,我们继续安排延迟任务(使用ScheduledExecutorService.schedule)。每个任务检查我们是否仍然是 RUNNING,如果是,则将单个循环迭代加入队列(使用 ExecutorService.submitExecutor.execute)。然后它为下一次迭代安排一个新的延迟任务(与 Timer 不同,您可以使用它来代替,但要以另一个线程为代价)。

如果状态为 PAUSED,我们将继续安排延迟任务以继续检查转换回 RUNNING 状态。但是我们不会为实际的循环迭代安排工作项。

如果状态是 STOPPED 那么我们将停止安排任何更多的延迟任务。

状态变量可以存储在一个简单的 volatile 字段中,该字段可以是静态的(如果只有其中一个)或者可以包含在表示 "thread"(实际上不再是单个线程,但从调用者的角度来看可能类似于一个线程)。