【问题标题】:strange bug - how to pause a java program?奇怪的错误 - 如何暂停 java 程序?
【发布时间】:2012-03-24 16:31:19
【问题描述】:

我正在尝试:

  1. 在 jLabel 中显示文本,
  2. 等待两秒钟,
  3. 然后在 jLabel 中写一个新文本

这应该很简单,但我遇到了一个奇怪的错误: 第一个文本永远不会写入,应用程序只需等待 2 秒,然后显示最终文本。这是示例代码:


private void testButtonActionPerformed(java.awt.event.ActionEvent evt) {    
    displayLabel.setText("Clicked!");

    //  first method with System timer

    /*
    long t0=  System.currentTimeMillis();
    long t1=  System.currentTimeMillis();
            do{
                t1 = System.currentTimeMillis();
            }
            while ((t1 - t0) < (2000));
     */    

    // second method with thread.sleep()

    try {
        Thread.currentThread().sleep(2000);
    } catch (InterruptedException e) {}

    displayLabel.setText("STOP"); 
}

使用此代码,文本“Clicked!”从不显示。我只有 2 秒 - 暂停,然后是“停止”文本。 我尝试将系统计时器与循环或 Thread.sleep() 一起使用,但两种方法都给出了相同的结果。

【问题讨论】:

  • (叹气)不要阻塞 EDT(事件调度线程)。而不是调用 Thread.sleep(n) 实现 Swing Timer 或使用 SwingWorker 来执行长时间运行的任务。有关详细信息,请参阅Concurrency in Swing

标签: java swing concurrency


【解决方案1】:

只是为了提供有关 Andrew Thompson 评论的更多背景信息:EDT 负责处理 gui 更新。如果您使用Thread.sleep(...) 阻止它,这些更新也会被阻止。这就是您看不到第一个文本的原因 - EDT 无法对标签进行更新。

【讨论】:

    【解决方案2】:

    这是一个可运行的示例,可以满足您的需求。正如 Andrew Thompson 的评论所述,SwingWorker 是解决此问题的好方法。

    基本原则是永远不要阻塞事件调度线程。那是负责重绘 GUI 和响应用户交互的线程,所以如果你在 EDT 上做一些计算成本很高的事情,你的 GUI 将停止响应。

    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.util.concurrent.ExecutionException;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.SwingWorker;
    
    public class ButtonTest {
        public static void main(String[] args) {
    
            // create a frame and a button
            JFrame frame = new JFrame();
            final JButton button = new JButton("Button");
            frame.add(button);
    
            // add an action listener to the button
            button.addActionListener(new ActionListener() {
    
                @Override
                public void actionPerformed(ActionEvent arg0) {
    
                    // change the button text right away
                    button.setText( "Clicked" );
    
                    // create a SwingWorker which simply waits 2000 milliseconds
                    // simulating a computation being performed
                    SwingWorker<String, Object> worker = new SwingWorker<String, Object>() {
                        @Override
                        public String doInBackground() {
    
                            // it's safe to call Thread.sleep( ) here
                            // doInBackground is executed on a separate worker
                            // thread
                            try {
                                Thread.sleep(2000);
                            } catch (InterruptedException e) {
                            }
    
                            return "Done";
                        }
    
                        @Override
                        protected void done() {
    
                            // done() is executed back on the Swing thread
                            // so it's safe to updated the state of the button
                            try {
                                button.setText(get());
                            } catch (Exception e) { }
                        }
                    };
    
                    // run the worker
                    worker.execute();
                }
            });
    
            frame.setSize( 300, 300 );
            frame.setVisible( true );
        }
    }
    

    【讨论】:

    • 感谢 Andrew Thompson、戒指持有者、充满鳗鱼的气垫船和 ulmangt。最后我用 SwingWorker 解决了这个问题。
    【解决方案3】:

    您正在搞乱事件调度程序线程。 如您所见,这将导致意外的 UI 行为。如果您打算制作这些类型的动画,请务必阅读@Andrew Thompson 的建议,并且看看您是否可以阅读 - Filthy rich clients

    最好使用 Swing Timer,如下面的 curde-example 所示:(是的,它很粗糙,我不担心停止计时器等):

    public class DelayTest  extends JPanel{
        JLabel messageLabel = new JLabel();
        JButton actionButton = new JButton("Click Me");
        String[] messages = {"Clicked", "Stop!"};
        int i=0;
        public DelayTest(){
            super();
            add(messageLabel);
            add(actionButton);
            actionButton.addActionListener(new ActionListener() {
    
                @Override
                public void actionPerformed(ActionEvent arg0) {
                    Timer timer = new Timer(1000, new ActionListener() {
    
                        @Override
                        public void actionPerformed(ActionEvent arg0) {
                            if(i<=1)
                                messageLabel.setText(messages[i++]);
                        }
                    });
                    timer.start();
    
                }
    
            });
        }
    
    }
    

    编辑
    为什么不停止计时器:

               @Override
               public void actionPerformed(ActionEvent evt) {
                  if (i <= 1) {
                     messageLabel.setText(messages[i++]);
                  } else {
                     ((Timer)evt.getSource()).stop();
                  }
               }
            });
    

    【讨论】:

    • @TerraNova993 这是最简单的方法,Swing Timer 存在的原因 +1
    • @HovercraftFullOfEels - 它不会 - 我在我的例子中提到过......这是一个粗略的。我不想提供量身定制的解决方案,我更希望 TerraNova993 了解基础知识并正确处理。
    猜你喜欢
    • 1970-01-01
    • 2022-01-12
    • 1970-01-01
    • 2023-03-11
    • 2012-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多