Поток выполнения при использовании Swing


Я только начинаю разбираться с GUI-программированием на java. Вот тривиальная программа (из "Head First Java" О'Рейли), которая на первый взгляд кажется простой для понимания, но есть аспект, который я не понимаю.

import javax.swing.*;

public class Test {
    public static void main(String[] args) {

    JFrame frame=new JFrame();
    JButton button = new JButton("click me");

    frame.getContentPane().add(button); 
        frame.setSize(300,300);
    frame.setVisible(true);
    }
}

Эта простая программа, будучи скомпилирована и запущена, откроет окно с кнопкой на нем.

Чего я не понимаю, так это того, что происходит с потоком исполнения. Когда я запускаю эту программу, статический основной метод класса Test выполняется, все команды в main() выполняются - так почему же процесс не завершается после появления окна? Почему я все еще сижу на том, что выглядит как бесконечная петля? Что такое зацикливание?

Если я добавлю строку

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

Затем я нахожу результат еще более imcomprehensible. Теперь, конечно, программа завершается. как только я закрою окно. Но опять-таки я не понимаю, почему. Фрейм будет находиться в стеке, но я не вижу, где находится поток программы, и просто существование чего-то в стеке отсутствует. достаточно, чтобы сохранить программу живой, не так ли? Мне не хватает чего-то фундаментального, что, насколько я могу судить, не описано в книге, которую я читаю. Я немного удивлен этим - "Head first Java" до сих пор была очень хороша в указании тонкостей и объяснении того, что на самом деле происходит, но, похоже, не затрагивает этот момент (по крайней мере, я не заметил).

2 4

2 ответа:

Почему процесс не завершается после появления окна?

Потому что виртуальная машина Java завершает работу только после завершения всех потоков, не являющихся демонами. Хотя это и не очевидно, на самом деле в вашей программе есть два потока: основной поток и поток диспетчеризации событий , который делает все, что связано с компонентами Swing GUI. Поток диспетчеризации событий продолжается до тех пор, пока видны все компоненты графического интерфейса.

Собственно программа, хотя и может работа, это неправильно, потому что вы создаете и получаете доступ к компонентам Swing из основного потока. Вы должны выполнять всю работу GUI в потоке диспетчеризации событий. То есть это должно быть что-то вроде:

public static void main(String[] args) {

  EventQueue.invokeLater(new Runnable() {
    JFrame frame=new JFrame();
    JButton button = new JButton("click me");

    frame.getContentPane().add(button); 
    frame.setSize(300,300);
    frame.setVisible(true);
  });
}

Процесс Java завершается, когда умирает последний не-демонический поток. Обычно существует только один, поток main. При отображении компонентов Swing запускаются дополнительные недемоновские потоки для диспетчеризации событий и вывода графического интерфейса. Они завершаются, когда последний компонент верхнего уровня удаляется. В вашем примере поток main умирает после выхода из метода main. Вы можете посмотреть на потоки с помощью отладчика или jvisualvm из инструментов JDK.

Остальная часть потока графического интерфейса является событием управляемый. Когда вы, например, нажимаете на кнопку закрытия кадра, событие генерируется и отправляется соответствующим слушателям в потоке диспетчеризации событий.

Установка JFrame.EXIT_ON_CLOSE в качестве операции закрытия по умолчанию похожа на добавление прослушивателя событий по умолчанию к кадру. Довольно жесткий, он просто отключает JVM без уважения к остальной части состояния приложения.