如何在不阻止EDT的情况下等待EDT中的对象创建?

1 java concurrency swing edt swingworker

我在Java库中使用API​​,它从事件派发线程调用,并要求我返回一个完全初始化的UI组件.它看起来像这样:

public JDialog createDialog();
Run Code Online (Sandbox Code Playgroud)

但是我只能在从数据库加载后填充对话框,有时可能需要10秒.通常我会在后台线程中执行此操作,但由于此方法是从EDT调用的,因为我必须返回对话框,这将无效.这是第三方库,所以我无法改变方法,但有什么办法可以避免阻止EDT吗?

Sco*_*nes 5

"已初始化"不一定与"已填充"相同."已初始化"通常表示对象已完全构建,但可能没有任何数据."填充"当然意味着数据存在且任何数据获取任务都已完成.因此,可以为您的第三方库提供完全初始化的JDialog,而不需要任何数据.

我总是喜欢解决这个问题的方法是创建一个显示忙消息或进度条等的自定义JDialog,然后在另一个线程中请求数据.返回数据时,我将忙信息替换为数据(在EDT上!).至于如何在后台线程中执行请求,我建议使用SwingWorkers.我喜欢SwingWorker在我的自定义JDialog中使用private 来处理doInBackground()方法中的请求,并处理方法中与Display相关的任务done().这样做将确保与显示相关的任务仅在EDT上发生,并且与数据库相关的任务仅在EDT之外发生.如果您想要使用SwingWorkers相当不错的介绍,请查看Sun关于工作线程的教程.一个简单的例子是:

public class DBDIalog extends JDialog{
     private JLabel busyLabel = new JLabel("Fetching data from DataBase");

     public DBDialog(){
         //do your initialization stuff here
     }

     private class DBFetcher extends SwingWorker<Void,DBInfo>{

        @Override
        protected DBInfo doInBackground() throws Exception{
            return fetchDataFromDB(); //or whatever database call to make
        }

        @Override
        protected void done(){
           try{
               DBInfo info = get();
           //replace your busy label with your DBInfo
           }catch(InterruptedException e){
              //do appropriate thread interrupted stuff
           }catch(ExecutionException e){
              //do appropriate general error handling stuff 
           }

        }
     }
}
Run Code Online (Sandbox Code Playgroud)

但要记住一些事项:该done()方法不是抽象的,因此您不需要覆盖它.不过你应该这样.如果您的doInBackground()实现抛出异常,那么除非done()被覆盖,否则将吞下该异常.此外,doInBackground()除非您使用,否则不要从内部更改GUI SwingUtilities.invokeLater(Runnable),因为doInBackground()从与EDT不同的线程执行,并且从后台线程进行GUI更改会要求奇怪且无法解释的错误.

什么时候应该使用?与其他编程任务不同,GUI中响应时间过长的点要短得多 - 我通常看到写下的数字大约是250ms.如果您的任务花费的时间比这要长,那么它应该在后台线程中.在你的情况下,10秒肯定应该在后台线程中,但是你已经知道:)

编辑:

看到你的评论,我看到我的大多数帖子都没有实际意义.但是,您仍然可以使用SwingWorker:

让您的SwingWorker执行数据检索,并在该done()方法中,让它从数据构建JDialog并将该对话框交给您的第三方库.