Java: есть ли SwingUtilities.invokeNowOrLaterIfEDT() или что-то подобное?

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

Вот типичный 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, то было бы очень легко выстрелить себе в ногу, вызвав такой служебный метод для долгих вычислений, который бы тогда все будет поставлено в очередь, а не правильно многопоточно.


person SyntaxT3rr0r    schedule 06.02.2010    source источник
comment
ты уверен, что это то, что ты хочешь. потому что любой исполняемый объект, переданный в invokeNowOrLaterIfEDT, никогда не будет запущен в EDT.   -  person Suraj Chandran    schedule 06.02.2010
comment
какова цель, которую вы хотите достичь, возможно, тогда мы сможем предоставить вам альтернативы. И нет, Java не предоставляет такого API.   -  person Suraj Chandran    schedule 06.02.2010
comment
@Suraj: то, чего я хочу достичь, четко указано в вопросе: цель состоит в том, чтобы запустить что-то в потоке, который НЕ является EDT, поэтому в приведенном мной примере убедитесь, что, как вы заметили, никогда не запускайте в EDT .   -  person SyntaxT3rr0r    schedule 06.02.2010
comment
.. в этом случае прочитайте мой ответ ниже :)   -  person Suraj Chandran    schedule 06.02.2010


Ответы (3)


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

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

person Suraj Chandran    schedule 06.02.2010
comment
@Suraj: о, я не совсем ясно выразился в своем вопросе ... Я понимаю, почему и вы, и Эш дали мне такие ответы, я отредактирую свой вопрос. - person SyntaxT3rr0r; 06.02.2010

Другой вариант (в дополнение к Suraj's) заключается в использовании SwingWorker.

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

person Ash    schedule 06.02.2010

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

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

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 одновременных задач, даже если некоторые из них являются длительными.

person David Moles    schedule 07.10.2011