【问题标题】:Java: mytextarea.setText("hello") + Thread.sleep(1000) = strange resultJava:mytextarea.setText("hello") + Thread.sleep(1000) = 奇怪的结果
【发布时间】:2011-01-03 07:24:05
【问题描述】:

我有这样的事情:

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 秒,然后显示“hello 4”。

有什么想法吗?

【问题讨论】:

  • sleep 是一个静态方法。最好使用Thread.sleep(..) 而不是Thread.currentThread().sleep(..)。两者在语法上都是正确的。

标签: java user-interface multithreading


【解决方案1】:

是的 - 你基本上阻塞了 UI 线程,所以它永远不会真正更新。

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

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

您还应该注意Thread.sleep 是一个静态方法。您正在使用它,就好像它是一个实例方法一样。诚然,您碰巧将其称为“在”当前线程上,但您的用法表明您认为:

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

会使 new 线程休眠。它不会 - 它会使当前线程休眠,因为 Thread.sleep always 就是这样做的。 IMO,Java 允许您以这种方式调用静态方法是错误的 - 如果您使用的是 Eclipse,则可以选择将其设为警告或错误。

【讨论】:

    【解决方案2】:

    就像 Jon Skeet 的回答中所解释的那样,您应该使用计时器,因为您无法阻止 EDT 并期望 UI 更新。下面是您重写的示例 sn-p 以使用 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】:

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

      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();
                  }
              });
          }
      }
      

      【讨论】:

        【解决方案4】:

        另一种不阻塞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 不是线程安全的,也就是说,未标记为线程安全的 Swing 方法只能在 EDT 上调用。 setText() 是线程安全的,所以上面的代码没有问题。

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

        更多详情见:Swing's Threading Policy

        【讨论】:

        • 小心使用这种方法只调用线程安全的方法。 JTextComponent.setText 是线程安全的,尽管大多数 Swing 方法不是。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-11-28
        • 2012-12-16
        • 2012-12-06
        • 2016-08-24
        • 2018-01-24
        • 2021-03-01
        • 2014-08-17
        相关资源
        最近更新 更多