Java 的 Swing 线程

Posted

技术标签:

【中文标题】Java 的 Swing 线程【英文标题】:Java's Swing Threading 【发布时间】:2011-02-03 14:19:47 【问题描述】:

我的理解是,如果我启动另一个线程来执行某些操作,我将需要SwingUtilities.invokeAndWaitSwingUtilities.invokeLater 在我处于所述线程时更新 GUI。如果我错了,请纠正我。

我想要完成的事情相对简单:当用户单击提交时,我想(在执行任何操作之前)禁用提交按钮,执行操作,并在操作结束时重新启用按钮.我执行该操作的方法在返回结果时直接更新 GUI(显示结果)。

此操作基本上是查询服务器并返回一些结果。

到目前为止我所拥有的是:

boolean isRunning = false;

synchronized handleButtonClick() 
  if ( isRunning == false ) 
    button.setEnabled( false );
    isRunning = true;
    doAction();
  


doAction() 
  new Thread() 
    try 
      performAction(); // Concern A
     catch ( ... ) 
      displayStackTrace( ... ); // Concern B
     finally 
      SwingUtilities.invokeLater ( /* simple Runnable to enable button */ );
      isRunning = false;
    
  

对于上述两个问题,我是否必须使用SwingUtilities.invokeAndWait,因为它们都会更新 GUI?所有 GUI 更新都围绕更新 JTextPane。我是否需要在我的线程中检查我是否在 EDT 上,如果是,我可以调用我的代码(无论它是否更新 GUI)而不使用 SwingUtilities.invokeAndWait

编辑:这是我现在正在做的事情:

handleButtonClick() 
  if ( isRunning == true )
     return;
  disable button;
  SwingWorker task = new MyTask();
  task.execute();


...inside MyTask
doInBackground() 
  return performAction();


done() 
  result = get();
  enable button;
  isRunning = false;
  interpret result (do most of the GUI updates here);

虽然performAction() 进行了一些 GUI 更新,但我已将其包装在:

if ( SwingUtil.isEDT() )
  doGUIupdate()
else
  SwingUtil.invokeLater( new Runnable() 
    run() 
      doGUIupdate();
    
   );

希望这是朝着正确方向迈出的一步,如果您认为有更好的方法来处理我的情况,请发表评论。

【问题讨论】:

使用 SwingWorker。 java.sun.com/javase/6/docs/api/javax/swing/SwingWorker.html 我的印象是 SwingWorker 应该用于“长”的东西,而 SwingUtilities 用于“快速”的东西? 因为 SwingUtilities 在“UI”线程上执行,它必须用于快速实用的东西;否则它可能会阻止用户界面。两者都不会在“另一个线程”中执行。 【参考方案1】:

我将简单的Thread 保留在EventQueue.invokeLater(...) 中,并且运行顺利...

java.awt.EventQueue.invokeLater(new Runnable() 
    public void run()

        new Thread(new Runnable()
            public void run()

                try
                    EdgeProgress progress = EdgeProgress.getEdgeProgress();
                    System.out.println("now in traceProgressMonitor...");
                    while(true)
                        // here the swing update
                        if(monitor.getState() == ProgressMonitor.STATE_BUSY)
                            System.out.println(monitor.getPercentDone()/2);
                            progress.setProgress(monitor.getPercentDone()/2);
                        else
                            break;
                        
                        Thread.sleep(5);
                    
                catch(InterruptedException ie)

            
        ).start();

    
);

【讨论】:

【参考方案2】:

在我看来,您几乎不应该使用invokeAndWait()。如果某些事情需要一段时间,就会锁定您的 UI。

对这种事情使用SwingWorker。看看Improve Application Performance With SwingWorker in Java SE 6。

【讨论】:

很棒的链接,通过了一半,它对我有很多启示! invokeAndWait() 不会阻塞 UI - 它会阻塞辅助线程,直到 UI 有机会运行传入的 Runnable。 invokeAndWait() 实际上会检查以确保它没有被 EDT 调用(这会导致锁定)。当然我应该说使用 invokeAndWait() 通常意味着程序员没有正确使用事件驱动(观察者)编程...... 使用invokeAndWait经常会导致死锁。 死链接,请考虑更新! @Redandwhite 感谢指出死链接。注意:我通过在旧链接的 Oracle 页面中粘贴其名称来找到该文章...【参考方案3】:

您应该考虑使用 SwingWorker,因为它不会阻塞 UI 线程,而 SwingUtilities 两个方法都会在 EDT 线程上执行,从而阻塞 UI。

【讨论】:

SwingWorker 在 EDT 上执行其 done() 方法。这与启动您自己的非 EDT 线程相同,该线程在您完成后调用 SwingWorker.invokeLater() 来更新 UI。 SwingWorker 使用起来比自己滚动方便一点。 @Scott 你是说 SwingUtilities 吗? 天啊!那应该是 SwingUtilities.invokeLater()。谢谢你的收获。不过,我确实同意 SwingWorker 现在是首选。

以上是关于Java 的 Swing 线程的主要内容,如果未能解决你的问题,请参考以下文章

Java Swing事件处理机制

java swing多线程

由java中的另一个线程刷新GUI(swing)

Swing:关于Java界面编程的第一课,如何正确的处理界面中的线程

如何通过按下 Java Swing 按钮来停止线程?

Java Swing 线程:执行逻辑命令时 GUI 挂起