ScheduledThreadPoolExecutor запускает Swingworker только один раз

ScheduledThreadPoolExecutor (который реализует ScheduledExecutorService), по-видимому, запускает класс SwingWorker только один раз при использовании метода ScheduleAtFixedRate. Исходный код довольно длинный, поэтому я сделал новый код, который дает те же результаты, что и ниже.

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 запускает SwingWorker только один раз? И что я могу сделать, чтобы SwingWorker запускался каждые 30 миллисекунд, как указано в коде?


person ICanCYou    schedule 21.08.2016    source источник


Ответы (2)


Хотя SwingWorker реализует интерфейс Runnable в соответствии с его API раздел о методе doInBackground():

Обратите внимание, что этот метод выполняется только один раз.

Таким образом, в то время как его внутренний метод run() может выполняться неоднократно, doInBackground() будет выполняться только один раз. Кроме того, метод run() помечен как final в SwingWorker, поэтому вы не можете переопределить его для многократного вызова doInBackground.

Лучшее решение — вообще не использовать SwingWorker, а использовать более простой класс, производный от Runnable.

person Hovercraft Full Of Eels    schedule 21.08.2016
comment
Итак, doInBackground() вызывается только один раз для каждого класса, а не один раз для каждого экземпляра этого класса? А также, что, если вам абсолютно необходимо использовать SwingWorker, потому что вы используете объекты Swing и вам нужно вызвать repaint() (здесь, конечно, не показано, но в исходном коде)? - person ICanCYou; 22.08.2016
comment
@ICanCYou: он вызывается один раз для каждого экземпляра - вы создаете только один экземпляр. - person Hovercraft Full Of Eels; 22.08.2016
comment
@ICanCYou: также вы можете создать несколько SwingWorkers, если это необходимо, или можете поставить вызовы Swing в очередь на поток событий, используя SwingUtilieis. Обратите внимание, что repaint() не нужно вызывать в потоке событий. - person Hovercraft Full Of Eels; 22.08.2016
comment
@Hovercraft Full Of Eels Может ли исполнитель запланировать новый экземпляр SwingWorker каждые 30 миллисекунд, используя метод scheduleAtFixedRate()? - person ICanCYou; 22.08.2016
comment
@ICanCYou: вы можете создать SwingWorker в Runnable, который вызывает служба-исполнитель, но это кажется излишним. Почему бы просто не поставить код Swing в очередь по мере необходимости. - person Hovercraft Full Of Eels; 22.08.2016
comment
@ICanCYou: или вы можете выполнить старомодный цикл while (true) с Thread.sleep(...) в самом методе doInBackground SwingWorker - person Hovercraft Full Of Eels; 22.08.2016
comment
В итоге я использовал Runnable вместо SwingWorker, как вы предложили в своем ответе. Мой код теперь работает нормально. Спасибо. - person ICanCYou; 22.08.2016
comment
работает вечно (если создан контейнер верхнего уровня) - person mKorbel; 22.08.2016

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 запустится только один раз, если вы попытаетесь запустить его снова, он просто вернется.

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);
    }
}
person prunesquallor    schedule 21.08.2016