【问题标题】:Is running a ExecutorService inside a SwingWorker a good practice?在 SwingWorker 中运行 ExecutorService 是一种好习惯吗?
【发布时间】:2012-04-12 04:31:01
【问题描述】:

考虑以下代码:

        SwingWorker<Void, Void> sworker = new SwingWorker<Void, Void>() {

        @Override
        protected Void doInBackground() throws Exception {
            ExecutorService executor = Executors.newFixedThreadPool(5);
            try {
                for (int j = 0; j < 5; j++) {
                    Callable<Object> worker = new MyCallableImpl();
                    Future<Object> future = executor.submit(worker); 
                    array[j] = future.get();
                }
            } catch (InterruptedException e) {
                // some code here
            } catch (ExecutionException e) {
                // some code here
            }
                // some code here
            executor.shutdown();
            return null;
        }

    };
    sworker.execute();

正如我在标题中所说:这是在 SwingWorker 的 doInBackground() 方法中调用 ExecutorService 的好习惯吗?它适用于我(JDK1.7),GUI 没有被阻塞,并且来自 Executor 池的多个线程在后台运行,但我仍然有一些疑问......

【问题讨论】:

    标签: java swing concurrency


    【解决方案1】:

    进一步mre的回应。这没有任何意义,因为您的执行实际上是单线程的。 doInBackground 将提交给执行者并等待该单个任务完成然后提交另一个。

    您应该以相同的方式提交,但将返回的Futures 存储在某种列表中,然后在提交所有任务后获取每个列表。

    我不介意 doInBackground 像 mre 那样异步提交这些作业。如果您尝试提交多个任务并且在任何给定时间只提交了 N 个任务,那么您绝对不应该通过SwingWorker.doInBackground 执行此操作。我认为使用ExectorService + SwingUtilities.invokeLater 是更好的方法。

    为了澄清任何混淆,invokeLater 应该只在 ExecutorService 中的任务完成并且它需要做的只是更新 UI 组件时在这里使用。

    编辑:解决您的评论的示例

    protected Void doInBackground() throws Exception {
        ExecutorService executor = Executors.newFixedThreadPool(5);
        List<Future> futures = ...;
        try {
            for (int j = 0; j < 5; j++) {
                Callable<Object> worker = new MyCallableImpl();
                futures.add(executor.submit(new Callable<Object>(){
                     public Object call(){
                        //expensive time consuming operation
                        final String result = ...;//result from consuming operation
                        SwingUtilities.invokeLater(new Runnable(){
                            public void run(){
                                 jLabel.setText(result);
                            }
                        });
                        return new Object();
                     }
                ));
            }
            for(Future<Object> f :futures)f.get();
            executor.shutdown();
        return null;
    }
    

    注意invokeLater 是如何进行简单更新的?这不会导致您的 EDT 冻结。

    【讨论】:

    • 我会试试这个。顺便说一句,SwingUtilities.invokeLater() 和 EventQueue.invokeLater() 有什么区别?
    • 在标准的 Java 发行版中,什么都没有。 SwingUtilities.invokeLater 只是代表EventQueue.invokeLater。只需将所有必要的功能耦合到 SwingUtilities 中。
    • 我在invokeLater中封装了所有更改GUI的调用,在一个匿名的Runnable中,GUI仍然被锁定。我做错了什么?顺便说一句:我的 sn-p 中的整个代码在事件侦听器调用的方法中运行(按下按钮后)。
    • 你能发布你的代码吗?通过 invokeLater 更新的 GUI 通常应该是一个单独的分配。所有昂贵的工作都应该在 ExecutorService 中,我将用一个示例进行编辑
    • @DoktorNo 随意看看我的编辑,看看我在说什么。
    【解决方案2】:
    • 可以从 Executor 执行 SwingWorkers 实例

    • 必须接受 Executor 不关心 SwingWorkers 生命周期,反之亦然

    • 必须为 SwingWorker 实现 PropertyChangeListener

    • 例如here

    【讨论】:

      【解决方案3】:

      上面的代码对我来说没有多大意义。

      如果此处的目标是确保在执行长时间运行的任务时 GUI 保持响应,则无需使用 ExecutorService,因为 SwingWorker 已经提供了该机制。

      【讨论】:

      • 是的,我知道,但我想在 SwingWorker 中运行多个线程(Callables)。如果没有在 SwingWorker 中不必要的包装 Executor,我该怎么做?
      • 我会完全摆脱SwingWorker。如果这些任务中的任何一个修改了 Swing 组件,请使用 SwingUtilities.invokeLater 包装调用
      • 是的,他们正在修改 Swing 组件(代码未显示,以避免混淆)。我会在此期间尝试您的解决方案。
      • 我将 doInBackground 中的一段代码封装在 SwingUtilities.invokeLater() 中,在匿名 Runnable 中,GUI 被阻塞。这不是我想要的......
      猜你喜欢
      • 1970-01-01
      • 2012-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-08
      • 2022-01-20
      • 2012-08-14
      • 1970-01-01
      相关资源
      最近更新 更多