ScheduledThreadPoolExecutor 只运行一次 Swingworker

ScheduledThreadPoolExecutor only runs Swingworker once

ScheduledThreadPoolExecutor(实现 ScheduledExecutorService)在使用 ScheduleAtFixedRate 方法时似乎只 运行ning SwingWorker class 一次。原来的代码有点长,所以我做了一个新的代码,产生了下面相同的结果。

import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

public class ScheduledThreadPoolExecutorTest extends SwingWorker<Void, Void>{
    @Override
    protected Void doInBackground() {
        System.out.println("Yay!");
        return null;
    }

    @Override
    protected void done() {
        try {
            get();
        } catch(Exception e) {
            e.printStackTrace();
        }
        System.out.println("Woohoo!");
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
                executor.scheduleAtFixedRate(new ScheduledThreadPoolExecutorTest(), 0, 30, TimeUnit.MILLISECONDS);
            }
        });
    }
}

这会产生结果:

Yay!
Woohoo!

为什么ScheduledThreadPoolExecutor 运行ning SwingWorker只有一次?我该怎么做才能使 SwingWorker 运行 按照代码中的指示每 30 毫秒执行一次?

虽然 SwingWorker 确实实现了 Runnable 接口,但根据其关于 doInBackground() 方法的 API 部分:

Note that this method is executed only once.

因此,虽然其内部 run() 方法可能会重复 运行,但 doInBackground() 只会 运行 一次。不仅如此,run() 方法在 SwingWorker 中被标记为 final,因此您无法重写它以多次调用 doInBackground

更好的解决方案是根本不使用 SwingWorker,而是使用更简单的 Runnable 派生的 class。

SwingWorker 扩展了 Runnable,但是,它使用 FutureTask 来运行它的计算。

来自 javadoc:

A cancellable asynchronous computation.  This class provides a base
implementation of {@link Future}, with methods to start and cancel
a computation, query to see if the computation is complete, and
retrieve the result of the computation.  The result can only be
retrieved when the computation has completed; the {@code get}
methods will block if the computation has not yet completed.  Once
the computation has completed, the computation cannot be restarted
or cancelled (unless the computation is invoked using
{@link #runAndReset}).

也就是说,FutureTask 只会 运行 一次,如果您再次尝试 运行 它,它只会 return.

public void run() {
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);
            }
            if (ran)
                set(result);
        }
    } finally {
        // runner must be non-null until state is settled to
        // prevent concurrent calls to run()
        runner = null;
        // state must be re-read after nulling runner to prevent
        // leaked interrupts
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}