Java Swing Thread改变UI - 并发症

时间:2012-12-23 02:53:02

标签: java multithreading swing user-interface event-dispatch-thread

我试了好几个小时..我有一个线程改变了我的UI的JTextField,这完全破坏了UI。线程(让我们称之为线程A)由ActionListener生成。 .setText()函数调用在由线程A创建的额外线程(B)中。线程B是SwingUtilitis.invokeAll()和/或SwingUtilities.invokeAndWait()的参数。我试过了两个。这里有一些代码可以让它更清晰。

这是我创建线程A的ActionListener - 当然缩短了:

public void actionPerformed(ActionEvent evt) {
    Object source = evt.getSource();
    if (source == window.getBtn_Search()) {
        Refresher refresh = new Refresher();
        refresh.start();
    }
}

这是我的线程A,后来将线程B放入EDT队列:

public class Refresher extends Thread implements Runnable {

private int counter = 0;
private UI window = null;
private int defRefresh = 0;

@Override
public void run() {
    while(true){
        -bazillion lines of code-
                do {
                    try {
                        Refresher.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if(window.canceled()) break;
                    UI.updateCounter(window.getLbl_Status(), (Configuration.getRefreshTime()-counter));
                    counter++;
                } while (counter <= Configuration.getRefreshTime());
             - more code-
    }
}
}

UI.updateCounter(...)将线程B排入EDT。

public static void updateCounter(final JLabel label, final int i) {
    try {
        SwingUtilities.invokeAndWait( 
            new Runnable() {
                public void run() {
                    label.setText("Refreshing in: " + i + " seconds.");
                }
            }
        );
    } catch (InvocationTargetException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

现在,当调用最后一个函数时,一切都搞砸了。我尝试了几个小时不同的东西,没有任何效果我也尝试过使用SwingWorker,但有些甚至根本没有发生。

4 个答案:

答案 0 :(得分:0)

一般来说,标签不是很擅长显示更改的文字:它们的宽度变化以及带有它的布局。

使用只读JTextField,可能是样式的适当更改,可能是更好的解决方案。

答案 1 :(得分:0)

我认为您创建的中间JPanel可能算作验证根。因此,当您调用revalidate()时自动发生的setText()不会导致任何布局更改高于JPanel父级别。

我认为您实际上不需要面板,因为JLabel可以包含图标和文本。请参阅the tutorial

所以我的建议是删除面板,如果它们用于某个目的,请确保面板上的isValidateRoot()返回false。

答案 2 :(得分:0)

invokeAndWait()尝试允许发布要在EDT上执行的Runnable任务,但它会阻止当前线程并等待EDT完成执行任务。

但是invokeAndWait()中存在死锁的可能性,因为在任何代码中都存在创建线程相互依赖性的问题。 如果调用代码保持一些代码调用的锁(显式或隐式) 通过invokeAndWait()需要,那么EDT代码将等待非 EDT代码释放锁定,因为非EDT代码不可能发生 正在等待EDT代码完成,应用程序将挂起。

正如我们在这里看到的,修改等待非传递的JLabel组件 EDT代码。

我们可以使用

invokeLater()需要 关心创建和排队包含Runnable的特殊事件。该事件按照收到的顺序在EDT上处理,就像任何其他事件一样。 到时候,运行Runnable的run()方法调度它。

SwingUtilities.invokeLater(new Runnable() {
public void run() {
label.setText("Refreshing in: " + i + " seconds.");
}
});

isEventDispatchThread()如果调用代码当前正在EDT上执行则返回true,否则返回false。

Runnable code= new Runnable() {
                public void run() {
                    label.setText("Refreshing in: " + i + " seconds.");
                }
            }
        );

if (SwingUtilities.isEventDispatchThread()) {
code.run();
} else {
SwingUtilities.invokeLater(code);
}

答案 3 :(得分:-1)

更改label的文字时,您至少应在repaint()/revalidate()最顶层的容器上调用label,触发重新布局,假设为{{ 1}}在文本更改时正确调用label / invalidate()