Java:mytextarea.setText(“hello”)+ Thread.sleep(1000)=奇怪的结果

时间:2010-01-09 18:01:23

标签: java user-interface multithreading

我有这样的事情:

for(int i=0; i<5; i++){
    mytextarea.setText("hello " + i);
    try{
        Thread.currentThread().sleep(1000); //to give time for users to read
    } catch(Exception e){}
}

我希望它会在文本区显示“hello 0”,等待1秒,然后显示“hello 1”,然后等待1秒等等。

但发生的情况有所不同,等待5秒,然后显示“你好4”。

有什么想法吗?

4 个答案:

答案 0 :(得分:12)

是的 - 你基本上阻止了UI线程,所以它永远不会实际更新。

在UI线程中睡觉是一个非常糟糕的主意。

如果你想做这样的事情,你应该使用Timer。 (我假设您正在使用Swing。如果没有,请编辑您的问题以指明您正在使用的UI框架。)

您还应注意Thread.sleep是一种静态方法。您正在使用它,就好像它是一个实例方法。不可否认,你碰巧在当前线程上“打电话”,但你的用法表明你认为:

Thread t = new Thread(...);
t.start();
t.sleep(1000);

会使 new 线程休眠。它不会 - 它将使当前线程休眠,因为这是Thread.sleep 始终所做的事情。 IMO让Java以这种方式调用静态方法是错误的 - 如果你正在使用Eclipse,那么可以选择将其作为警告或错误。

答案 1 :(得分:4)

当您的代码等待时,不会处理任何事件

http://java.sun.com/docs/books/tutorial/uiswing/concurrency/index.html

阅读javax.swing.SwingUtilities.invokeAndWait()和invokeLater()的javadoc,这可能会有所帮助

编辑:感谢Jon和Samuel将所有想法放在一起:

public class Swing extends JPanel {
    JTextField textField;
    static JTextArea textArea;
    static int line = 1;

    public Swing() {
        super(new BorderLayout());
        textArea = new JTextArea(5, 20);
        add(textArea);
    }

    private static void createAndShowGUI() {
        JFrame frame = new JFrame("TextDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new Swing());
        frame.pack();
        frame.setVisible(true);

        ActionListener taskPerformer = new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                textArea.append("Hello " + line++ + "\n");
            }
        };
        if (line < 5) {
            new Timer(1000, taskPerformer).start();
        }
    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
}

答案 2 :(得分:4)

与Jon Skeet的回答中解释的一样,您应该使用计时器,因为您无法阻止EDT并期望UI更新。下面是您重写为使用Swing计时器的示例代码段。

ActionListener action = new ActionListener() {
    int i = 0;

    public void actionPerfomed(ActionEvent e) {
        mytextarea.setText("hello " + i++);
    }
};
new javax.swing.Timer(1000, action).start();

有关计时器功能的详细信息,请参阅Swing教程中的How to Use Swing Timers

答案 3 :(得分:2)

不阻止Event Dispatch Thread(EDT)的另一种方法是启动一个新的线程:

Thread thread = new Thread(new Runnable() {
    @Override
    public void runt() {
        for (int i=0; i<5; i++) {
            mytextarea.setText("hello " + i);
            try {
                Thread.sleep(1000); //to give time for users to read
            } catch (InterruptedException e) {
                break;    // interrupt the for
            }
        }
    }
});
thread.start();

编辑:
一般来说,Swing不是线程安全的,也就是说,只有在EDT上才能调用未标记为线程安全的Swing方法。 setText()是线程安全的,所以在上面的代码中没问题。

要在EDT上运行代码,请使用javax.swing.SwingUtilities(或java.awt.EventQueue)中的invokeAndWait()invokeLater()

有关详细信息,请参阅:Swing's Threading Policy