开发者

Timeout a task with Java's SwingWorker

I am trying to implement a SwingWorker开发者_JS百科 class within my application. Is there a way to set a length of time that after which, the SwingWorker "times out"? I was thinking that maybe throwing an OutOfTime exception that I can catch and then deal with. I'm just not sure how to implement it.

Thanks for all your help!


Why not embed your task within a Runnable, drop it into a new single-threaded ExecutorService and then perform a get() on the resulting Future with an appropriate timeout. That would give you your timeout functionality, since get() will throw an exception if the job doesn't complete in time.


The short answer is "it's hard", depending on what your requirements are. I highly recommend reading Java Concurrency In Practice.

The basic thing you could do would be to (a) make sure your SwingWorker's Runnable is interrupt-friendly, and (b) set a Timer (or use the blocking get() call Brian mentioned) to cancel your Future.


The inner class MySwingWorker may well do what you need:

package com.misc;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class FutureStuffGUI extends JFrame {
/**
 * Provides a variant of SwingWorker which operates with a timeout.
 * 
 * @param <T>
 */
private static abstract class MySwingWorker<T> {

    private T result;
    private Exception raised;

    /**
     * Constructor.
     * 
     * @param timeout
     * @param timeUnit
     */
    public MySwingWorker(final long timeout, final TimeUnit timeUnit) {
        result = null;
        raised = null;

        System.out.println(Thread.currentThread().getName() + " starting");
        final FutureTask<T> future = new FutureTask<T>(new Callable<T>() {
            public T call() throws Exception {
                System.out.println(Thread.currentThread().getName() + " running");
                T result = doInBackground();
                return result;
            }
        });
        System.out.println(Thread.currentThread().getName() + " future: " + future);
        final Thread runner = new Thread(null, future, "FutureThread");
        Thread watcher = new Thread(null, new Runnable() {

            @Override
            public void run() {
                runner.start();
                try {
                    result = future.get(timeout, timeUnit);
                } catch (Exception ex) {
                    raised = ex;
                }
                SwingUtilities.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        assert SwingUtilities.isEventDispatchThread();
                        done();
                    }
                });
            }
        }, "WatcherThread");
        watcher.start();
    }

    /**
     * Implement this method as the long-running background task.
     * 
     * @return
     * @throws Exception
     */
    abstract protected T doInBackground() throws Exception;

    /**
     * This method is invoked from the UI Event Dispatch Thread on completion or timeout.
     */
    abstract protected void done();

    /**
     * This method should be invoked by the implementation of done() to retrieve
     * the result.
     * 
     * @return
     * @throws Exception
     */
    protected T get() throws Exception {
        assert SwingUtilities.isEventDispatchThread();
        if (raised != null) {
            throw raised;
        } else {
            return result;
        }
    }
}

public FutureStuffGUI() {
    super("Hello");
    init_components();
}

private void init_components() {
    JPanel panel = new JPanel();
    JButton button = new JButton("Press");
    panel.add(button);
    add(panel);
    pack();

    button.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            new MySwingWorker<String>(5, TimeUnit.SECONDS) {

                @Override
                protected String doInBackground() throws InterruptedException {
                    assert !SwingUtilities.isEventDispatchThread();
                    System.out.println(Thread.currentThread().getName() + " doInBackground");
//                        if (true) { throw new RuntimeException("Blow up"); }
                    Thread.sleep(6 * 1000);
                    return "Hello world!";
                }

                @Override
                protected void done() {
                    assert SwingUtilities.isEventDispatchThread();
                    String result;
                    try {
                        result = get();
                        System.out.println(Thread.currentThread().getName() + " done; result: " + result);
                    } catch (Exception ex) {
                        System.out.println(Thread.currentThread().getName() + " done; errored:");
                        ex.printStackTrace();
                    }
                }
            };
        };
    });
}

public static void main(String[] args) {
    FutureStuffGUI ui = new FutureStuffGUI();
    ui.setVisible(true);
}

}


Any particular reason for not using the SwingWorker.get(long, java.util.concurrent.TimeUnit) ? It will throw TimeoutException that you can easily handle in your application.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜