小程序 - init(),EDT和线程

时间:2013-10-27 20:48:10

标签: java applet event-dispatch-thread

Java不是我的母语,我一直在与这个问题作斗争。

基本上,我发现直接从init()调用方法switchApplets()与从init()生成的新线程中调用它之间存在行为差异。

从新线程内部调用它的结果是新applet的屏幕显示 - 除非用户调整大小或最小化浏览器。如果在init()结束时调用,则新UI会立即呈现,而无需用户输入任何内容。但这不是一个选择,因为它不会等待线程完成准备工作。

修剪代码:

public class PreLoader extends Applet implements AppletStub {

static JProgressBar pBar = null;
static JLabel message;

public void switchApplets() {
    try {
        Class main_class = Class.forName("MainClass");
        Applet main_applet = (Applet)main_class.newInstance();
        removeAll();
        setSize(0,0);
        setLayout(new GridLayout(1,0));
        add(main_applet);
        main_applet.init();
        main_applet.start();
        main_applet.setStub(this);
    }
    catch (Exception e) {
    }
}

public void init() {

    pBar = new JProgressBar(0, 100);
    pBar.setValue(0);
    pBar.setStringPainted(true);

    message = new JLabel("Beginning work!");

    add(message);
    add(pBar);

    FlowLayout flow = new FlowLayout();

    setLayout(flow);

    Thread t = new Thread ( new Runnable () {
        public void run ()
        {
            longRunningFunction1();
            longRunningFunction2();
            message.setText("Work complete! Stand by..");
            switchApplets(); //does NOT work as intended from here
            return;
        }
    } );
    t.start();
    //switchApplets(); //works as intended if called HERE
}

public void longRunningFunction1() {
    //perform some tasks, advance progress bar
}

public void longRunningFunction2() {
    //perform some tasks, advance progress bar
}

public void start() {
    return;
}

public void appletResize(int width, int height) {
    return;
}

}

我尝试使init()等待线程完成,以便我可以从那里调用switchApplets(),但这只会阻止E​​DT并阻止UI更新。还尝试使用SwingUtilities的invokeLater / invokeAndWait,但即使switchApplets()在EDT上运行,似乎必须直接从init()(或至少运行的线程init)调用它才能获得所需的效果

为什么在新线程中调用switchApplets()会导致稍微不同(和不需要的)UI行为?

1 个答案:

答案 0 :(得分:0)

  

从新线程内部调用它的结果是新的applet白屏 - 直到/除非用户调整大小或最小化他们的浏览器。

尝试在错误的线程上执行UI代码可能会造成死锁。

  

我尝试使init()等待线程完成,以便我可以从那里调用switchApplets(),但这只会阻止E​​DT并阻止UI更新。

你走在正确的轨道上。您只需要从EDT调用switchApplets(),并且只能在另一个线程上完成工作。

在长时间运行的函数完成后,您确定在生成的线程中尝试使用invokeLater()或invokeAndWait()吗?自从我做applet以来已经有很长一段时间了,但是我不知道任何applet特定的原因,为什么它不起作用,并且它可以在任何其他情况下工作。即,

public void run()
{
    longRunningFunction1();
    longRunningFunction2();
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            message.setText("Work complete! Stand by..");
            switchApplets();
        }
    });
}

但是,最合适的方法是使用SwingWorker而不是手动创建的线程。 SwingWorker(它几乎不应该是它应该是众所周知的)的设计完全是为了在单独的线程上执行后台任务,同时仍然能够使用进度更新和结果更新GUI。如,

new SwingWorker<Void,Void>() {
    @Override
    protected Void doInBackground() { // is called on a background thread
        longRunningFunction1();
        longRunningFunction2();
        return null;
    }

    @Override
    protected void done() { // is called on the Swing thread
        message.setText("Work complete! Stand by..");
        switchApplets();
    }
}.execute();

Void这是因为SwingWorker还能够返回结果并发送中间进度更新,但此示例不使用这些功能。

您表示您的长时间运行功能也在更新进度条。这是另一件应该只在Swing线程上发生的事情。在实践中,你经常可以在没有它的情况下离开,但它很狡猾。您的进度更新可以使用SwingUtilities.invoke方法之一,也可以使用SwingWorker的机制;要么应该工作。 (SwingWorker本身提供了两种不同的方法:调用addPropertyChangeListener(Swing线程)和setProgress(后台线程),或调用publish(后台线程)并覆盖process (摇摆线程)。)

另外,一个小建议:如果处理一个检查过的异常是不方便的(或者不可能有效地这样做),而不是捕捉和忽略它,你至少应该抓住&amp;将其重新抛出为未经检查的例外:

catch (Exception e) {
    throw new RuntimeException(e);
}

这样,任何异常的堆栈跟踪和错误消息都不会丢失。