Поток диспетчеризации событий (EDT) — это фоновый поток, используемый в Java для обработки событий из очереди событий графического пользовательского интерфейса Abstract Window Toolkit (AWT) . Это пример общей концепции программирования, управляемого событиями , которая популярна во многих других контекстах, помимо Java, например, в веб-браузерах или веб-серверах .
События в основном являются событиями обновления, которые заставляют компоненты пользовательского интерфейса перерисовывать себя, или событиями ввода с устройств ввода, таких как мышь или клавиатура. AWT использует однопоточную модель рисования , в которой все обновления экрана должны выполняться из одного потока. Поток диспетчеризации событий является единственным допустимым потоком для обновления визуального состояния видимых компонентов пользовательского интерфейса. Обновление видимых компонентов из других потоков является источником многих распространенных ошибок в программах Java , использующих Swing . [1] Поток диспетчеризации событий называется primordial worker в Adobe Flash и UI-потоком в SWT , .NET Framework и Android .
Программное приложение обычно состоит из нескольких потоков и одной структуры данных GIT. Это означает, что GIT является общей структурой данных, и необходима некоторая синхронизация, чтобы гарантировать, что только один поток получает к ней доступ в каждый момент времени. Хотя AWT и Swing предоставляют методы ( небезопасные для потоков ) для создания и доступа к компонентам GUI, и эти методы видны всем потокам приложения, аналогично в других фреймворках GUI, только один поток Event Dispatching имеет право выполнять эти методы. [2] [3] [4] Поскольку программисты часто упускают это требование, сторонние Look and Feels , такие как Substance, заходят так далеко, что отказываются создавать экземпляры любого компонента Swing, когда он не запущен в потоке Event Dispatch, [5] , чтобы предотвратить такую ошибку кодирования. Доступ к GUI сериализован, и другие потоки могут отправлять некоторый код для выполнения в EDT через очередь сообщений EDT .
То есть, как и в других фреймворках GUI, поток диспетчеризации событий проводит свою жизнь, перекачивая сообщения: он поддерживает очередь сообщений действий, которые должны быть выполнены через GUI. Эти запросы отправляются в очередь системой и любым потоком приложения. EDT потребляет их один за другим и отвечает обновлением компонентов GUI. Сообщения могут быть известными действиями или включать обратные вызовы, ссылки на пользовательские методы, которые должны быть выполнены с помощью EDT.
Важное требование, предъявляемое ко всем сообщениям, заключается в том, что они должны выполняться быстро, чтобы GUI оставался отзывчивым. В противном случае цикл сообщений блокируется и GUI зависает.
Существуют различные решения для отправки кода в EDT и выполнения длительных задач без блокировки цикла.
Компоненты GUI поддерживают списки обратных вызовов, называемых прослушивателями, которые обычно заполняются при создании компонентов. EDT запускает прослушиватели, когда пользователь каким-либо образом возбуждает компоненты (нажатие кнопки, перемещение мыши, выбор элемента, потеря фокуса, изменение размера компонента и т. д.).
Для коротких задач, которые должны периодически или в определенное время получать доступ к GUI/изменять его, javax.swing.Timer
используется. Его можно рассматривать как невидимый компонент GUI, слушатели которого зарегистрированы для срабатывания в определенное время(а).
Эквиваленты
System.Windows.Forms.Timer
- .NET Frameworkflash.utils.Timer
- Adobe FlashДругие потоки приложения могут передавать некоторый код для выполнения в потоке диспетчеризации событий с помощью SwingUtilities
вспомогательных классов (или EventQueue
если вы используете AWT ). Отправленный код должен быть обернут в Runnable
объект. Два метода этих классов позволяют:
SwingUtilities.invokeAndWait(Runnable)
или EventQueue.invokeAndWait(Runnable)
)SwingUtilities.invokeLater(Runnable)
или EventQueue.invokeLater(Runnable)
)из потока диспетчеризации событий.
Метод invokeAndWait()
никогда не следует вызывать из потока диспетчеризации событий — это вызовет исключение . Метод SwingUtilities.isEventDispatchThread()
или EventQueue.isDispatchThread()
можно вызвать, чтобы определить, является ли текущий поток потоком диспетчеризации событий.
Код, предоставляемый посредством invokeLater
и invokeAndWait
в EDT, должен быть максимально быстрым, чтобы предотвратить зависание. Обычно они предназначены для доставки результата длительного вычисления в GUI (пользователю).
Выполнение задачи в другом потоке и представление результатов в EDT можно объединить с помощью шаблона проектирования worker . javax.swing.SwingWorker
Класс, разработанный Sun Microsystems , является реализацией шаблона проектирования worker и, начиная с Java 6, входит в стандартный дистрибутив Swing. SwingWorker обычно вызывается из прослушивателя событий, выполняемых EDT, для выполнения длительной задачи, чтобы не блокировать EDT.
SwingWorker < Document , Void > worker = new SwingWorker < Document , Void > () { public Document doInBackground () выдает исключение IOException { return loadXML (); // тяжелая задача } public void done () { try { Document doc = get (); display ( doc ); } catch ( Exception ex ) { ex . printStackTrace (); } } }; worker . execute ();
Если вы используете Groovy и groovy.swing.SwingBuilder
, вы можете использовать doLater()
, doOutside()
, и edt()
. Тогда вы можете записать это проще, вот так:
doOutside { def doc = loadXML () // тяжелая задача edt { display ( doc ) } }
System.ComponentModel.BackgroundWorker
- .NET Frameworkflash.system.Worker
- Adobe Flashandroid.os.AsyncTask
- АндроидSwingWorker обычно создается EDT для длительных задач при обработке событий обратного вызова (прослушивателя). Создавая рабочий поток, EDT продолжает обработку текущего сообщения, не дожидаясь завершения рабочего потока. Часто это нежелательно.
Часто ваш EDT обрабатывает действие компонента GUI, которое требует от пользователя сделать выбор с помощью другого диалога, например JFileChooser, который всплывает, остается отзывчивым, пока пользователь выбирает свой вариант, и действие продолжается с выбранным файлом только после нажатия кнопки «ОК». Видите ли, это занимает время (пользователь отвечает в течение нескольких секунд), и вам нужен отзывчивый GUI (сообщения все еще закачиваются в EDT) в течение всего этого времени, пока EDT блокируется (он не обрабатывает более новые, например JFileChooser, сообщения в очереди до закрытия диалога и завершения текущего действия компонента). Порочный цикл разрывается, когда EDT входит в новый цикл сообщений, который отправляет сообщения как обычно, пока не придет «модальный диалог не завершится» и обычная обработка сообщений не возобновится с заблокированной позиции в действии компонента.
Проект Foxtrot с открытым исходным кодом эмулирует цикл сообщений Swing, обеспечивая «синхронный» механизм выполнения произвольных пользовательских задач, который выполняется только после того, как исполнитель завершит задачу.
button.addActionListener ( new ActionListener () { public void actionPerformed ( ActionEvent e ) { button.setText ( " Спящий режим ... " ) ; String text = null ; try { text = ( String ) Worker . post ( new Task () { public Object run () throws Exception { Thread . sleep ( 10000 ); return "Slept !" ; } }); } catch ( Exception x ) ... кнопка .setText ( текст ) ;что-тоЕще (); } });
Начиная с версии Java 1.7, Java предоставляет стандартное решение для пользовательских вторичных циклов сообщений , предоставляя createSecondaryLoop () в системной EventQueue ().
javax.swing
( Документация Javadoc по API Swing )java.awt
(документация Javadoc API AWT )