Где вызывается поток отправки событий?

Я читал, что весь код, который создает компоненты Swing и обрабатывает события, должен запускаться потоком диспетчеризации событий. Я понимаю, как это достигается с помощью метода SwingUtilities.invokeLater(). Рассмотрим следующий код, в котором инициализация графического интерфейса выполняется в самом методе main.

public class GridBagLayoutTester extends JPanel implements ActionListener {   
    public GridBagLayoutTester() {
        setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();

        JButton button = new JButton("Testing");
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.anchor = GridBagConstraints.WEST;
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.gridwidth = 1;
        button.addActionListener(this);
        add(button, gbc);
    }

    public void actionPerformed(ActionEvent e) {
        System.out.println("event handler code");
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("GridBagLayoutDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);    
        Container contentPane = frame.getContentPane();
        contentPane.setLayout(new BorderLayout());
        contentPane.add(new GridBagLayoutTester(), BorderLayout.CENTER);
        frame.setSize(800, 600);
        frame.pack();
        frame.setVisible(true);
        System.out.println("Exiting");
    }   
}

Как получилось, что этот код работает идеально? Мы создаем JFrame и вызываем множество других методов в основном потоке. Я не понимаю, где именно здесь проявляется EDT (какой код он выполняет?). Конструктор класса GridBagLayoutTester также вызывается из метода main, что означает, что EDT его не запускает.

Короче

  1. Когда начинается EDT? (запускает ли JVM EDT вместе с основным методом, если EDT вообще запускается при выполнении этого кода?)
  2. Выполняется ли код обработчика событий для кнопки на EDT?

person Stormshadow    schedule 03.09.2010    source источник


Ответы (4)


Код работает отлично, потому что вы создаете фрейм в основном потоке до того, как EDT получит возможность взаимодействовать с ним. Технически вы никогда не должны этого делать, но технически вы можете в этих конкретных обстоятельствах, потому что вы не можете взаимодействовать с JFrame, пока он не станет видимым.

Главное, что нужно знать, это то, что компоненты Swing не являются потокобезопасными. Это означает, что они не могут быть изменены более чем из одного потока одновременно. Это решается путем обеспечения того, чтобы все модификации исходили из EDT.

EDT - это поток, посвященный взаимодействию с пользователем. Любые события, генерируемые пользователем, всегда запускаются на EDT. Любые обновления пользовательского интерфейса запускаются на EDT. Например, когда вы вызываете Component.repaint(), вы можете вызывать это из любого потока. Это просто устанавливает флаг, чтобы пометить компонент как требующий окраски, и EDT сделает это в своем следующем цикле.

EDT запускается автоматически и довольно тесно связан с реализацией системы. Это хорошо обрабатывается внутри JVM. Обычно он соотносится с одним потоком в оконной системе, который обрабатывает взаимодействие с пользователем. Конечно, это в значительной степени зависит от реализации. Приятно то, что вам не нужно об этом беспокоиться. Вам просто нужно знать - если вы взаимодействуете с какими-либо компонентами Swing, делайте это на EDT.

Точно так же есть еще одна важная вещь. Если вы собираетесь выполнить какую-либо длительную обработку или блокировку для внешнего ресурса, и вы собираетесь сделать это в ответ на событие, сгенерированное пользователем, вы должны запланировать его запуск в собственном потоке вне EDT. Если вы этого не сделаете, пользовательский интерфейс будет заблокирован, пока он ожидает запуска длительной обработки. Отличные примеры - загрузка из файлов, чтение из базы данных или взаимодействие с сетью. Вы можете проверить, находитесь ли вы в EDT (полезно для создания нейтральных методов, которые могут быть вызваны из любого потока) с помощью метода SwingUtilities.isEventDispatchThread().

Вот два фрагмента кода, которые я довольно часто использую при написании Swing-программирования для EDT:

void executeOffEDT() {
  if (SwingUtilities.isEventDispatchThread()) {
    Runnable r = new Runnable() {
      @Override
      public void run() {
        OutsideClass.this.executeOffEDTInternal();
      }
    };
    new Thread(r).start();
  } else {
    this.executeOffEDTInternal();
  }
}

void executeOnEDT() {
  if (SwingUtilities.isEventDispatchThread()) {
    this.executeOnEDTInternal();
  } else {
    Runnable r = new Runnable() {
      @Override
      public void run() {
        OutsideClass.this.executeOnEDTInternal();
      }
    };
    SwingUtilities.invokeLater(r);
  }
}
person Erick Robertson    schedule 03.09.2010
comment
Что касается Component.repaint (), я сомневаюсь, что это только устанавливает флаг, он фактически ставит в очередь событие рисования (которое затем будет обработано EDT). - person jfpoilpret; 03.09.2010
comment
Схема такая же. Вам не нужно знать, как это обрабатывается, чтобы успешно использовать EDT. - person Erick Robertson; 03.09.2010
comment
так что вызов frame.setVisible () выполняется на EDT? - person Stormshadow; 03.09.2010
comment
Нет. В вашем примере весь метод main () выполняется в основном потоке. Единственная строка кода, выполняемая на EDT, находится в методе actionPerformed. Стандарт, который я видел, - это создать своего рода метод initializeUserInterface (), обернуть его в runnable и вызвать invokelater для него из основного потока. Затем вы перемещаете туда все содержимое основного метода. Это 100% правильный способ справиться с этим. - person Erick Robertson; 03.09.2010
comment
@Erick, так что в основном компоненты Swing действительно могут быть созданы и отрисованы из основного потока, хотя лучше всего переместить этот код в EDT. - person Stormshadow; 03.09.2010
comment
да. Если вы попытаетесь сделать это до того, как он будет виден на экране, он не сломается, потому что никто не сможет взаимодействовать с ним, пока вы его настраиваете. Если вы попытаетесь сделать это после того, как это будет видно на экране, вы рискуете выбросить исключения параллелизма. - person Erick Robertson; 04.09.2010

Поток отправки событий, как следует из его названия, вызывается Swing каждый раз, когда необходимо обработать событие.

В приведенном вами примере кнопка «Тестирование» автоматически вызывает метод actionPerformed, когда необходимо обработать событие действия. Таким образом, содержимое вашего метода actionPerformed будет вызываться потоком диспетчеризации событий.

Чтобы ответить на два ваших последних вопроса:

  • EDT запускается автоматически при загрузке инфраструктуры Swing. Вам не нужно беспокоиться о запуске этого потока, JRE выполнит эту задачу за вас.
  • Код обработчика событий выполняется EDT. Все события, генерируемые вашим интерфейсом Swing, объединяются, и EDT отвечает за их выполнение.
person Vivien Barousse    schedule 03.09.2010

1) Я не знаю, в new JFrame или в setVisible, но он инициализируется по запросу, и то, что конец основного метода (над основным потоком процесса) не завершает процесс. EDT был запущен и находится в блокированном цикле, ожидая следующего события.

2) Окончательно. Этот цикл получает от ОС событие, находит JButton и сообщает ему, что событие было запущено. Затем кнопка вызывает слушателей. Все, что происходит в EDT.

Вы можете просмотреть код Swing, который вы вызываете, когда хотите убить процесс (или закрыть главное окно), чтобы посмотреть, где завершается EDT ... это может дать вам подсказку (я сделаю это позже! :)

person helios    schedule 03.09.2010

EDT-поток запускается, после чего вы сначала вызываете setVisible(true);, если он еще не запущен, например. Или если вы вызываете SwingUtilities.invokeAndWait() или SwingUtilities.invokeLater() методы.

См. http://www.leepoint.net/JavaBasics/gui/gui-commentary/guicom-main-thread.html.

person Igor Kostomin    schedule 04.07.2013