Как работает stream отправки событий?

С помощью людей в stackoverflow мне удалось получить следующий рабочий код простого обратного отсчета GUI (который просто отображает окно, отсчитывающее секунды). Моя основная проблема с этим кодом – это материал invokeLater .

Насколько я понимаю invokeLater , он отправляет задачу в stream диспетчеризации событий (EDT), а затем EDT выполняет эту задачу всякий раз, когда она «может» (что бы это ни значило). Это правильно?

Насколько я понимаю, код работает следующим образом:

  1. В main методе мы используем invokeLater для отображения windows (метод showGUI ). Другими словами, код, отображающий окно, будет выполнен в EDT.

  2. В main методе мы также запускаем counter а счетчик (по конструкции) выполняется в другом streamе (так что это не в случае диспетчерского streamа). Правильно?

  3. counter выполняется в отдельном streamе и периодически вызывает updateGUI . updateGUI должен обновить GUI. И графический интерфейс работает в EDT. Таким образом, updateGUI также должен быть выполнен в EDT. Именно по этой причине код для updateGUI заключен в invokeLater . Это правильно?

Мне непонятно, почему мы вызываем counter из EDT. Во всяком случае, он не выполняется в EDT. Он запускается немедленно, там выполняется новый stream и counter . Итак, почему мы не можем вызывать counter в основном методе после блока invokeLater ?

 import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.SwingUtilities; public class CountdownNew { static JLabel label; // Method which defines the appearance of the window. public static void showGUI() { JFrame frame = new JFrame("Simple Countdown"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); label = new JLabel("Some Text"); frame.add(label); frame.pack(); frame.setVisible(true); } // Define a new thread in which the countdown is counting down. public static Thread counter = new Thread() { public void run() { for (int i=10; i>0; i=i-1) { updateGUI(i,label); try {Thread.sleep(1000);} catch(InterruptedException e) {}; } } }; // A method which updates GUI (sets a new value of JLabel). private static void updateGUI(final int i, final JLabel label) { SwingUtilities.invokeLater( new Runnable() { public void run() { label.setText("You have " + i + " seconds."); } } ); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { showGUI(); counter.start(); } }); } } 

Если я правильно понял ваш вопрос, вам интересно, почему вы не можете этого сделать:

 public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { showGUI(); } }); counter.start(); } 

Причина, по которой вы не можете этого сделать, заключается в том, что планировщик не дает никаких гарантий … только потому, что вы вызывали showGUI() а затем вы вызывали counter.start() , не означает, что код в showGUI() будет выполнен до код в методе запуска counter .

Подумайте об этом так:

  • invokeLater запускает stream, и этот stream назначает асинхронное событие на EDT, которому поручается создание JLabel .
  • счетчик – отдельный stream, который зависит от JLabel чтобы он мог вызвать label.setText("You have " + i + " seconds.");

Теперь у вас есть условие гонки: JLabel должен быть создан до начала counter streamа, если он не создан до начала встречного streamа, то ваш счетный stream будет вызывать setText на неинициализированном объекте.

Чтобы убедиться, что условие гонки устранено, мы должны гарантировать порядок выполнения и один из способов гарантировать, что он должен выполнять showGUI() и counter.start() последовательно в одном и том же streamе:

 public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { showGUI(); counter.start(); } }); } 

Теперь showGUI(); и counter.start(); выполняются из одного streamа, поэтому JLabel будет создан до запуска counter .

Обновить:

Q: И я не понимаю, что особенного в этой теме.
A: Код обработки событий Swing запускается в специальном streamе, известном как stream отправки событий. В этом streamе также работает большинство кода, который вызывает методы Swing. Это необходимо, потому что большинство методов объекта Swing не являются «streamобезопасными»: вызывать их из нескольких streamов подвергает streamу помехи или ошибки согласованности памяти. 1

Q: Итак, если у нас есть GUI, зачем нам запускать его в отдельном streamе?
Ответ: Вероятно, есть лучший ответ, чем мой, но если вы хотите обновить GUI из EDT (что вы делаете), то вам нужно запустить его из EDT.

Q: И почему мы не можем просто начать stream, как любой другой stream?
A: См. Предыдущий ответ.

В: Почему мы используем какой-то invokeLater и почему этот stream (EDT) начинает выполнять запрос, когда он готов. Почему он не всегда готов?
A: EDT может иметь некоторые другие события AWT, которые он должен обрабатывать. invokeLater Заставляет doRun.run () выполняться асинхронно в streamе диспетчеризации событий AWT. Это произойдет после того, как все ожидающие события AWT будут обработаны. Этот метод следует использовать, когда stream приложений должен обновлять графический интерфейс. 2

Вы фактически запускаете counter streamа из EDT. Если вы invokeLater counter.start() после блока invokeLater , счетчик, скорее всего, начнет работать до того, как графический интерфейс станет видимым. Теперь, поскольку вы создаете графический интерфейс в EDT, GUI не будет существовать, когда counter начнет его обновлять. К счастью, вы, похоже, перенаправляете обновления GUI в EDT, что верно, и поскольку EventQueue является очередью, первое обновление произойдет после создания графического интерфейса, поэтому не должно быть причин, почему это не сработает. Но в чем смысл обновления графического интерфейса, который еще не может быть видимым?

Что такое EDT?

Это хакерское решение вокруг множества проблем параллелизма, которые имеет Swing API;)

Серьезно, многие компоненты Swing не являются «streamобезопасными» (некоторые известные программисты дошли до вызова Swing «thread invile»). Имея уникальную цепочку, в которой все обновления добавляются к этим не зависящим от streamа компонентам, вы уклоняетесь от множества возможных проблем параллелизма. В дополнение к этому вы также гарантируете, что он должен запустить Runnable который вы проходите через него, используя invokeLater в последовательном порядке.

Затем некоторые nitpicking:

 public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { showGUI(); counter.start(); } }); } 

А потом:

В основном методе мы также запускаем счетчик, а счетчик (по конструкции) выполняется в другом streamе (так что это не в случае диспетчерского streamа). Правильно?

Вы действительно не запускаете счетчик в основном методе. Вы запускаете счетчик в методе run () анонимного Runnable который выполняется в EDT. Таким образом, вы действительно запускаете счетчик Thread из EDT, а не основной метод. Затем, поскольку это отдельный stream, он не запускается на EDT. Но счетчик определенно запускается на EDT, а не в Thread выполняющем main(...) метод.

Это ничтожный, но все же важный вопрос, который я думаю.

Это просто, это так

Шаг 1 . Создается начальная нить, также называемая основным streamом.

Шаг 2. Создайте исполняемый объект и передайте его invokeLate ().

Шаг 3. Это инициализирует GUI, но не создает графический интерфейс.

Шаг 4. InvokeLater () планирует созданный объект для выполнения на EDT.

Шаг 5. Создан GUI.

Шаг 6. Все происходящие события будут помещены в EDT.

  • Глобальный захват клавиатуры в приложении C #
  • Выделить событие с текстовым полем?
  • Определите, будет ли пользователь щелкать полосу прокрутки или содержимое (onclick для собственной полосы прокрутки)
  • Как добавить события в элементы управления, созданные во время выполнения в Excel с помощью VBA
  • Как использовать GWT EventBus
  • простое пользовательское событие
  • События C # и безопасность streamов
  • Добавление и удаление анонимного обработчика событий
  • Как распознать события касания, используя jQuery в Safari для iPad? Является ли это возможным?
  • Слабая модель обработчика событий для использования с lambdas
  • В обработчике событий C # почему параметр «отправитель» должен быть объектом?
  • Interesting Posts

    Меньше и Grep: получение цветных результатов при использовании трубы от grep до менее

    Самый простой способ разобрать ответ JSON

    Обработка перенаправления HttpClient

    Как указать разные каталоги вывода отладки / выпуска в файле QMake .pro

    Можно ли расширять расширения Google Chrome с помощью node.js?

    Как изменить форматирование чисел на оси с помощью ggplot?

    Каков наилучший способ подключения и использования базы данных sqlite из C #

    Как полностью отключить функции планшета в Windows 7?

    Не найден ресурс, который соответствует указанному имени (в диалогеCornerRadius со значением «? Android: attr / dialogCornerRadius»)

    Как подключиться к файлу MS Access (mdb) с помощью C #?

    byte поиск шаблона массива

    Почему Entity Framework не сможет использовать ToString () в операторе LINQ?

    Как включить функцию AutoStart для моего приложения в приложении безопасности Xiaomi Security App программно в android

    Цикл через карту с JSTL

    Нестатическая переменная не может ссылаться на статический контекст

    Давайте будем гением компьютера.