Как поток 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 хорошо себя ведет

Помимо этого, вы можете использовать различные другие механизмы.

  1. Вы могли бы использовать invokeAndWaitЧтоб поболтать меня к сказанному.
  2. Вы могли бы иметь runnable установить значение Future вместо прямой ссылки, и блокировать в главном потоке, вызывая будущее get метод.
  3. Вы могли бы иметь 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 передавать его модель или любую часть, которая ему нужна, в поток. Вместо наоборот. Таким образом, пользовательский интерфейс может дать отзыв о запущенном фоновом потоке. Но вы не можете позволить фоновому потоку касаться (записывать / изменять / изменять) членов этой модели, которые поток пользовательского интерфейса будет также читать / записывать одновременно. Поэтому, если вы планируете изменить модель в ответ на фоновый поток, опубликуйте ее обратно в поток пользовательского интерфейса, чтобы быть в безопасности.

Другие вопросы по тегам