Как поток Java синхронизируется с invokeLater()?
У меня есть поток без графического интерфейса, который запускает JFrame с помощью
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
cardReadPunchGUI = new IBM1622GUI(); // instantiate
cardReadPunchGUI.setVisible(true);
}
});
Часть конструктора IBM1622GUI создает для себя "модель", к которой моему потоку без графического интерфейса необходим доступ:
cardReadPunch = IBM1622GUI.getModel();
Как правильно синхронизировать мой поток без графического интерфейса с новым графическим интерфейсом, который был "вызван позже"? (Без синхронизации, конечно, IBM1622GUI.getModel()
просто имеет тенденцию возвращать ноль.)
4 ответа
Использование
javax.swing.SwingUtilities.invokeAndWait(Runnable doRun);
вместо.
Заставляет doRun.run() выполняться синхронно в потоке диспетчеризации событий AWT. Этот вызов блокируется до тех пор, пока не будут обработаны все ожидающие события AWT, и (затем) doRun.run() не вернется.
Ну, если у вас есть доступ к нему, вы всегда можете переместить эту конкретную логику за пределы потока Swing и на поток, который вызывает invokeLater
, Нет ничего небезопасного в том, что вы делаете там из потока Swing, предполагая, что конструктор для IBM622GUI
хорошо себя ведет
Помимо этого, вы можете использовать различные другие механизмы.
- Вы могли бы использовать
invokeAndWait
Чтоб поболтать меня к сказанному. - Вы могли бы иметь runnable установить значение
Future
вместо прямой ссылки, и блокировать в главном потоке, вызывая будущееget
метод. - Вы могли бы иметь
CountDownLatch
с начальным счетом 1, который выawait()
в вашей основной теме, иcountDown()
от качающейся нити.
Существует множество утилит, помогающих с синхронизацией.
Я бы посоветовал вам совместно использовать CountDownLatch, инициализированный в 1, как с потоками без GUI, так и с GUI.
Поток без графического интерфейса при запуске будет вызывать latch.await()
который переведет его в заблокированное состояние.
GUI поток будет вызывать latch.countDown()
когда он завершает свою инициализацию, после чего не-GUI-поток завершает работу из вызова await, и оба потока синхронизируются.
Обычно вы передаете параметры в поток. Запустите логику в фоновом режиме. А затем отправьте обратно любые изменения, которые вам нужно сделать, для любого из этих объектов или элементов пользовательского интерфейса в потоке пользовательского интерфейса, используя SwingUtilities.invokeLater(). Обычно я создаю простую утилиту, которая позволяет мне указать, что должно выполняться в фоновом потоке, а что - в потоке пользовательского интерфейса. SwingWorker - это то, что вы можете использовать, хотя я нахожу это чрезвычайно болезненным в использовании. Что-то простое, как это:
new AsyncThread<Param,T>() {
public T executeInBackground( Param param ) {
// do something long running
T result = // do something long running;
return T;
}
public void executeOnUI( T result ) {
// update the UI here, or modify the model, etc.
}
}.execute( param );
AsyncThread будет выполнять метод executeInBackground() в другом потоке. Затем внутренне он будет отправлять обратно в поток пользовательского интерфейса, используя SwingUtilities.invokeLater(). Затем executeOnUI будет выполняться в потоке пользовательского интерфейса. Метод execute() может создать поток для выполнения в фоновом режиме, обработки исключений и т. Д.
Я бы позволил GUI, возможно, запустить поток, и позволить GUI передавать его модель или любую часть, которая ему нужна, в поток. Вместо наоборот. Таким образом, пользовательский интерфейс может дать отзыв о запущенном фоновом потоке. Но вы не можете позволить фоновому потоку касаться (записывать / изменять / изменять) членов этой модели, которые поток пользовательского интерфейса будет также читать / записывать одновременно. Поэтому, если вы планируете изменить модель в ответ на фоновый поток, опубликуйте ее обратно в поток пользовательского интерфейса, чтобы быть в безопасности.