1

基于Oracle Concurrency guidelines我试图对我的GUI进行更新。我的问题是,GUI更新在EDT内的新线程中被调用,这导致了我很多不同的失败。如何让EDT等待后台线程?

下面这段代码就可以运行和我的样本GUI更新,一个简单的倒计时:

import java.awt.Component; 
import java.awt.Container; 
import java.awt.GridBagConstraints; 
import java.awt.GridBagLayout; 
import java.awt.Insets; 
import java.awt.Toolkit; 
import java.util.Timer; 
import java.util.TimerTask; 

import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.SwingUtilities; 
import javax.swing.SwingWorker; 

public class Chronometer2 { 

int COUNT = 5000; 


CountTask countTask; 


    private static final Insets INSETS = new Insets(1, 1, 1, 1); 


    public Chronometer2(){ 

     (countTask= new CountTask()).execute(); 

    } 


    private static void addComponent(Container container, Component component, int gridx, int gridy, 
      int gridwidth, int gridheight, int anchor, int fill) { 

      GridBagConstraints gbc = new GridBagConstraints(gridx, gridy, gridwidth, gridheight, 1.0, 1.0, 
     anchor, fill, INSETS, 0, 0); 

      container.add(component, gbc); 
      } 

    public int returnCount(){ 

     System.out.println("My count: " + COUNT); 
     return COUNT; 
    } 

     public void decreaseTime() { 
      COUNT--; 
     } 

     public int getTime() { 
      return COUNT; 
     } 


     private class CountTask extends SwingWorker<Void, Chronometer2>{ 

      @Override 
      protected Void doInBackground() throws Exception { 
       // TODO Auto-generated method stub 



       final JFrame messageFrame = new JFrame(); 
       final JLabel message = new JLabel("Next query in " + COUNT/1000 + " seconds."); 
       messageFrame.setLayout(new GridBagLayout()); 

       messageFrame.setTitle("Warning"); 
       messageFrame.setSize(500,100); 
       messageFrame.setLocationRelativeTo(null); 
       addComponent(messageFrame, message, 0, 0, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.CENTER); 
       messageFrame.setVisible(true); 

       final Timer timer = new Timer(); 
       timer.schedule(new TimerTask() { 

        @Override 
        public void run() { 


          decreaseTime(); 
            message.setText("Preventing connection block. Next query in " + COUNT/1000 + " seconds."); 

            if(getTime()==0){ 
             messageFrame.dispose(); 
             timer.cancel(); 
            } 

           } 
       }, 1, 1); 
       return null; 
      } 

     } 




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

} 

由于样品是从主线程,它的正常工作调用。但是从我的应用程序调用正在EDT内部进行,因为我需要在显示消息之前按下按钮并触发一个操作。我创建了一个新线程来调用Swing公用程序(它不能直接在EDT内部调用),即使我遇到问题,因为直到现在我仍无法让EDT等待后台线程完成,然后继续前进下一个查询。这是代码的EDT内伸展:

Thread test = new Thread(new Runnable() { 

         @Override 
         public void run() { 
          // TODO Auto-generated method stub 
          try { 
           SwingUtilities.invokeAndWait(new Runnable() { 

            @Override 
            public void run() { 
             // TODO Auto-generated method stub 
             new CountScreen(); 
            } 



           }); 
          } catch (InvocationTargetException e) { 
           // TODO Auto-generated catch block 
           e.printStackTrace(); 
          } catch (InterruptedException e) { 
           // TODO Auto-generated catch block 
           e.printStackTrace(); 
          } 
         } 
        }); 
        test.start(); 

我也tryed与wait()notifiy()和定时器任务,但问题是相同的处理这一点;下面是其他代码可以运行:

import java.awt.Component; 
import java.awt.Container; 
import java.awt.GridBagConstraints; 
import java.awt.GridBagLayout; 
import java.awt.Insets; 
import java.awt.Toolkit; 
import java.util.Timer; 
import java.util.TimerTask; 

import javax.swing.JFrame; 
import javax.swing.JLabel; 

public class Chronometer { 


    private static final Insets INSETS = new Insets(1, 1, 1, 1); 

    private int TIMER = 3000; 

public Chronometer(){ 

    double timeToNext = System.currentTimeMillis() + 30000; 
     double timeDifference = 30000; 

    final JFrame messageFrame = new JFrame(); 
    messageFrame.setLayout(new GridBagLayout()); 

    messageFrame.setTitle("Warning"); 
    final JLabel message = new JLabel("Next query in " + timeDifference/1000 + " seconds."); 
    messageFrame.setSize(500,100); 
    messageFrame.setLocationRelativeTo(null); 
    addComponent(messageFrame, message, 0, 0, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.CENTER); 
    messageFrame.setVisible(true); 

    System.out.println(Thread.currentThread().getName()); 

    final Timer timer = new Timer(); 
    timer.schedule(new TimerTask() { 

     @Override 
     public void run() { 



       synchronized (timer) { 


        decreaseTime(); 
         message.setText("Preventing connection block. Next query in " + TIMER/1000 + " seconds."); 

         if(getTime()==0){ 

          System.out.println("Im here"); 
          timer.notify(); 
          System.out.println("Im here 2"); 
          messageFrame.dispose(); 
          System.out.println("Im here 3"); 
          timer.cancel(); 
         } 

        } 
      } 
    }, 1, 1); 

    synchronized (timer) { 

     try { 

      timer.wait(); 
     } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 

    } 

     System.out.println("Done"); 

} 



private static void addComponent(Container container, Component component, int gridx, int gridy, 
     int gridwidth, int gridheight, int anchor, int fill) { 

     GridBagConstraints gbc = new GridBagConstraints(gridx, gridy, gridwidth, gridheight, 1.0, 1.0, 
    anchor, fill, INSETS, 0, 0); 

     container.add(component, gbc); 
     } 

public void decreaseTime() { 
    TIMER--; 
} 

public int getTime() { 
    return TIMER; 
} 


public static void main(String[] args){ 

Chronometer c = new Chronometer(); 
} 

} 

所以,我几乎3天纳闷,我怎么能解决这个问题?任何建议都非常受欢迎;提前致谢。

+1

阅读'SwingWorker' API。 http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html –

+0

认为我的SwingWorker子类应该在EDT内调用,而不是为它创建一个Thread并在其内运行,这是否正确? Swing公用事业? –

+1

SwingWorker的意义在于你不必管理自己的线程。不管你从哪个线程调用'execute','doInBackground'都将在后台线程上调用(由SwingWorker内部管理),'process'和'done'将在EDT上运行。 –

回答

1

我找到了我自己的答案。停止美国东部时间将永远不会是一种选择决不。如果你想处理像我这样的问题,解决方案比我想象的要简单得多。在触发器中触发操作后立即调用另一个线程来处理任何你想要的。这样可以防止EDT变得繁忙并冻结GUI。这样,您就可以尽可能以多种方式锁定线程,我的意思是同步方法,循环或您想使用的其他方法。我的解决方案我可以考虑阅读这篇文章:main and the GUI Event Dispatch。这真的是一篇很棒的文章,让我开始思考一个关于摆动和线程的新东西。希望这对你也有帮助。