Java: есть ли SwingUtilities.invokeNowOrLaterIfEDT (...) или подобное? - PullRequest
2 голосов
/ 06 февраля 2010

(обязательно прочитайте правку ниже, этот вопрос явно сбивает с толку, извините)

Вот типичный 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.

Существует ли что-то подобное в API Java по умолчанию?

Что было бы лучше: просто создавать новый поток каждый раз, когда он вызывается из EDT, как в примере выше, или иметь один дополнительный поток, в котором Runnables для выполнения вне EDT будет помещен в очередь или что-то еще?

EDIT : Точно так же, как SwingUtilities.invokeLater может вызываться или не вызываться из EDT, метод, который я имел в виду, может вызываться или не вызываться из EDT. Это должно выполнить что-то полностью асинхронное, которое может или не может быть вызвано действием пользователя на GUI / EDT. Я думаю, что SwingUtilities.invokeLater очень удобно иметь возможность запускать что-либо в EDT, не заботясь о том, когда вы вызываете это, если вы используете EDT или нет. И я думаю, что invokeNowOrLaterIfEDT(...), который я показал выше, очень удобен. Но я могу ошибаться в этом. Потребность в таком методе сумасшедшая? Теперь я начинаю сомневаться!?

РЕДАКТИРОВАТЬ ВТОРОЕ : Я не совсем понял, и я это понимаю, извините за это. Вещи, которые я хочу запускать асинхронно, а не из EDT, не являются сверхдлинным процессом, и нет необходимости в каких-либо обновлениях, таких как индикатор выполнения или что-либо, показывающее, что происходит. Это также не проблема, если он однопоточный / поставлен в очередь (очевидно, я об этом говорил в вопросе). Я не собираюсь использовать пул потоков для эффективного распределения нагрузки на различные ядра и т. Д. Просто это небольшие вычисления, которые не нужно запускать в EDT и которые могут вызываться из EDT или нет. Это действительно просто крошечный асинхронный материал, который я хочу выполнить вне EDT. Это не проблема, если все они находятся в очереди в одном потоке и т. Д.

Но теперь, благодаря вашим ответам, я также понимаю, что если бы такая вещь была реализована с использованием одного потока и очереди Runnable, то было бы очень легко выстрелить себе в ногу, долго вызывая такой служебный метод вычисление, которое затем будет поставлено в очередь вместо правильно многопоточного.

Ответы [ 3 ]

3 голосов
/ 06 февраля 2010
  1. Вы уверены, что это именно то, что вы действительно хотите сделать, потому что в любом случае ваш метод никогда не будет запущен на EDT.
  2. Теперь для вашего ответа, в зависимости от того, как часто и от какого количества потоков может вызываться этот метод, я думаю, вам следует использовать ThreadPool. Я бы предложил использовать ThreadPoolExecutor и другие API в параллельном пакете.

Одним из преимуществ использования java.util.concurrent API, такого как ThreadPoolExecutor и Executors, является то, что вы можете точно настроить множество параметров и всю систему пула, просто вызывая прямой API и без дополнительных усилий.

2 голосов
/ 07 октября 2011

Похоже, ваш вопрос сводится к тому, «лучше ли создавать новый поток для каждого асинхронного вызова или использовать выделенный поток / пул потоков»?

К сожалению, я думаю, что ответ "это зависит". Если вы знаете, что эти задачи короткие и редкие, то, вероятно, новый поток подойдет. Если вы беспокоитесь о длинных вычислениях, блокирующих друг друга, возможно, вам нужен пул потоков.

static ExecutorService ex = Executors.newCachedThreadPool();

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

Как отметил Сурадж , пакет concurrent дает вам много готовых вариантов. Executors.newCachedThreadPool() Я использовал выше, добавит темы по мере необходимости, повторно используя старые темы; но вместо этого вы можете попробовать Executors.newSingleThreadExecutor() и посмотреть, действительно ли длительные задачи действительно являются проблемой, или использовать Executors.newFixedThreadPool() с заданным числом потоков, чтобы убедиться, что вы всегда можете обрабатывать n одновременных задач, даже если некоторые из них давно работает.

2 голосов
/ 06 февраля 2010

Другой вариант (в дополнение к у Сураджа ) - использовать SwingWorker.

Этот класс специально разработан для того, что вы хотите сделать: взять (возможно длительный) процесс из EDT и выполнить его в отдельном потоке. SwingWorker имеет преимущество обработки всех переключений контекста потока, а также позволяет вам предоставлять обновления обратно в EDT (например, для отображения индикатора выполнения), не беспокоясь о потоке самостоятельно.

...