SwingUtilities.invokeLater: Вызов кода в потоке событий Swing
У меня возник вопрос, касающийся SwingUtilities.invokeLater
. Когда следует его использовать? Нужно ли применять его каждый раз, когда я обновляю компоненты GUI? Что именно он делает? Существует ли альтернатива, так как это не кажется интуитивно понятным и добавляет, по всей видимости, ненужный код?
4 ответ(ов)
Нет, не обязательно, если вы уже находитесь в потоке обработки событий (EDT), что всегда происходит, когда вы реагируете на действия пользователя, такие как клики и выборы. (Методы actionPerformed
и подобные всегда вызываются потоком EDT.)
Однако, если вы не находитесь в потоке EDT и хотите обновить графический интерфейс (например, если хотите обновить GUI из потока таймера или сетевого потока и т.д.), вам нужно запланировать обновление так, чтобы его выполнил поток EDT. Именно для этого предназначен этот метод.
Swing по сути не является потокобезопасным. То есть, все взаимодействия с этим API должны выполняться в одном потоке (в потоке EDT). Если вам нужно обновить GUI из другого потока (поток таймера, сетевой поток и т.д.), вы должны использовать такие методы, как тот, который вы упомянули (SwingUtilities.invokeLater, SwingUtilities.invokeAndWait и т.д.).
Каждое Swing-приложение имеет как минимум два потока:
- Основной поток, в котором выполняется приложение.
- EDT (Event Dispatching Thread) — это поток, который обновляет пользовательский интерфейс (UI), чтобы избежать его зависания.
Если вы хотите обновить пользовательский интерфейс, вы должны выполнять код в рамках EDT. Для этого существуют методы, такие как SwingUtilities.invokeLater
, SwingUtilities.invokeAndWait
, EventQueue.invokeLater
и EventQueue.invokeAndWait
, которые позволяют вам выполнять код в EDT.
Ваш вопрос связан с SwingUtilities.invokeLater
: когда следует его использовать?
Ключевое, что нужно понять, это то, что в Java есть отдельный поток (EDT), который обрабатывает события, связанные с Swing.
Вы должны использовать invokeLater()
, чтобы отобразить основной JFrame
настольного приложения (например), вместо того, чтобы пытаться сделать это в текущем потоке. Это также создаст контекст для корректного закрытия приложения позже.
На этом все для большинства приложений.
Должен ли я использовать его каждый раз, когда мне нужно обновить компоненты GUI? Что он именно делает?
Нет. Если вы изменяете компонент GUI, это вызовет событие, которое будет зарегистрировано для последующей обработки в Swing. Если для этого события есть слушатель, поток EDT вызовет его в дальнейшем. Вам не нужно использовать invokeLater()
, просто правильно установите своих слушателей на компоненты.
Имейте в виду, что этот поток — это тот же поток, который рисует кадры и т.д. на вашем экране. Поэтому слушатели не должны выполнять сложные/долгие/ресурсозатратные задачи, иначе ваш экран заморозится.
Существует ли альтернатива, так как это не звучит интуитивно и добавляет, казалось бы, ненужный код?
Вам не нужно писать больше кода, чем просто отобразить ваше приложение с помощью invokeLater()
+ слушатели, которые вас интересуют на компоненте. Остальное обрабатывает Swing.
Большинство событий, инициированных пользователем (клики, нажатия клавиш), уже будут выполняться в потоке обработки событий (EDT), так что вам не придется использовать SwingUtilities для этого. Это охватывает многие случаи, кроме вашего основного потока (main()
) и рабочих потоков, которые обновляют EDT.
Что делает SwingUtilities.invokeLater?
Что значит 'synchronized'?
Разница между интерфейсами Runnable и Callable в Java
Как распределяются потоки для обработки запросов Servlet?
Следует ли использовать отдельные экземпляры ScriptEngine и CompiledScript для каждого потока?