2

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

这是一个典型的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 调用它时只需创建一个新线程,就像上面的示例一样,或者有一个额外的线程在RunnablesEDT 之外执行,或者其他什么?

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

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

但是现在,多亏了你的回答,我也意识到,如果这样的事情是使用单线程和 Runnable 队列实现的,那么通过调用这样一个用于长时间计算的实用方法将很容易自取其辱然后全部排队而不是正确地多线程。

4

3 回答 3

3
  1. 你确定这是你真正想要做的,因为在任何情况下,在你的方法中,一个可运行对象永远不会在 EDT 上运行。
  2. 现在对于您的 anwser,取决于可以调用此方法的频率和线程数,我认为您应该使用 ThreadPool。我建议在并发包中使用ThreadPoolExecutor和其他 API。

使用像 ThreadPoolExecutor 和 Executors 这样的 java.util.concurrent API 的一个优点是,您可以通过简单地调用直接 API 来微调许多参数和整个池系统,而无需额外的努力。

于 2010-02-06T03:26:56.140 回答
2

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

不幸的是,我认为答案是“视情况而定”。如果您知道这些任务很短且很少见,那么一个新线程可能就可以了。如果您担心长时间的计算会相互阻塞,您可能需要一个线程池。

static ExecutorService ex = Executors.newCachedThreadPool();

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

正如 Suraj 所说,该concurrent软件包在这里为您提供了很多现成的选择。Executors.newCachedThreadPool()我上面用过的会根据需要添加线程,重用旧线程;但是您可以尝试Executors.newSingleThreadExecutor()查看长时间运行的任务是否真的有问题,或者使用Executors.newFixedThreadPool()一定数量的线程来确保您始终可以处理n并发任务,即使其中一些是长时间运行的。

于 2011-10-07T18:58:08.220 回答
2

另一种选择(除了Suraj 的)是使用SwingWorker.

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

于 2010-02-06T03:41:33.440 回答