Java:有没有SwingUtilities.invokeNowOrLaterIfEDT(...)或类似的?

时间:2010-02-06 03:09:53

标签: java swing asynchronous event-dispatch-thread

(请务必阅读下面的编辑,这个问题显然令人困惑,我很抱歉)

以下是典型的SwingUtilities.invokeLater电话:

  SwingUtilities.invokeLater( new Runnable() {
    public void run() {
       ...
    }
  } );

现在我希望得到SwingUtilities.invokeNowOrLaterIfEDT

当然我可以使用自己的实用程序类,如下所示:

public static void invokeNowOrLaterIfEDT( @NotNull Runnable r ) {
    if ( SwingUtilities.isEventDispatchThread() ) {
        Thread t = new Thread( r );
        t.start();
    } else {
        r.run();
    }
}

但是每次从EDT调用时都会创建一个新线程(并且必须等待调度新线程)。

另一种方法是使用一些阻塞队列将Runnables排入队列,在应用程序的生命周期中运行一个额外的线程(可能会被懒惰地实例化)。好处是每次我从EDT调用这样的方法时都不会经常创建线程。

默认的Java API中是否存在这样的内容?

什么会更好:只需在每次从EDT调用此线程时创建一个新线程,如上例所示,或者在EDT之外执行Runnables的另一个线程将被排队等等?

编辑:就像可能会或可能不会从EDT调用SwingUtilities.invokeLater一样,我想到的方法可能会也可能不会从EDT调用。它是执行完全异步的东西,可能会或可能不会从GUI / EDT上的用户操作触发。我认为SwingUtilities.invokeLater非常方便能够在EDT中运行某些东西而不必在你调用EDT时调用它。而且我认为上面展示的invokeNowOrLaterIfEDT(...)非常方便。但我可能错了。是否需要这种方法疯狂?现在我开始怀疑了??

编辑两次:我一点都不清楚,我意识到这一点,对不起。我想要异步运行而不是来自EDT的东西不是超长的过程,并且不需要任何更新,如进度条或任何显示正在发生的事情。如果它是单线程/排队的话也不是问题(显然,我在问题中说明了这一点)。我完全没有在线程池之后有效地将负载分散到各种内核等上。只是这些是小型计算,不需要在EDT上运行,也可以从EDT调用。它实际上只是我希望在EDT之外执行的微小异步内容。如果它们都在同一个线程上排队等等,那就不是问题了。

但是现在,多亏了你的答案,我也意识到如果使用单个线程和一个Runnable队列来实现这样的事情,那么通过长时间调用这样的实用方法就可以很容易地在脚中射击计算然后全部排队而不是正确的多线程。

3 个答案:

答案 0 :(得分:3)

  1. 您确定这是您真正想要做的事情,因为在您的方法中,任何情况下都不会在EDT上运行runnable。
  2. 现在,对于你的anwser,取决于调用此方法的频率和次数,我认为你应该使用ThreadPool。我建议在并发包中使用ThreadPoolExecutor和其他API。
  3. 使用java.util.concurrent API(如ThreadPoolExecutor和Executors)的一个优点是,您可以通过简单地调用直接API来微调大量参数和整个池系统,而无需额外的努力。

答案 1 :(得分:2)

另一个选项(除Suraj's之外)是使用SwingWorker

这个类是专门为你想要做的而设计的:从EDT中获取一个(可能是长时间运行的)进程并在一个单独的线程中执行它。 SwingWorker具有处理所有线程上下文切换的优点,还允许您将更新提供回EDT(例如,显示进度条),而不必担心自己的线程。

答案 2 :(得分:2)

看起来您的问题归结为“为每个异步调用创建新线程,还是使用专用线程/线程池更好”?

不幸的是,我认为答案是“它取决于”。如果您知道这些任务很短且很少见,那么新线程可能就好了。如果你担心长计算会相互阻塞,你可能需要一个线程池。

static ExecutorService ex = Executors.newCachedThreadPool();

public static void invokeOutsideEDT(Runnable r) {
    if (SwingUtilities.isEventDispatchThread()) {
       ex.submit(r);
    } else {
        r.run();
    }
}

As Suraj notedconcurrent套餐为您提供了许多现成的选择。 Executors.newCachedThreadPool()我在上面使用过,会根据需要添加线程,重用旧线程;但您可以尝试使用Executors.newSingleThreadExecutor()来查看长时间运行的任务是否确实存在问题,或者使用Executors.newFixedThreadPool()使用一定数量的线程来确保始终可以处理n个并发任务即使其中一些是长期运行的。