Я использую java.awt.FileDialog. Это модальный диалог, поэтому, когда я вызываю setVisible(true), он блокируется, пока я не выберу файл или иным образом не закрою окно.
Однако в документации для setVisible есть любопытная строка:
Можно вызывать этот метод из потока диспетчеризации событий, поскольку набор инструментов гарантирует, что другие события не будут заблокированы, пока этот метод заблокирован.
Мне было интересно, как это работает, поэтому я решил проверить это, запланировав некоторые другие события, пока диалог виден. Я использовал ScheduledExecutorService, чтобы запускать событие раз в секунду.
Согласно моему эксперименту, один и тот же поток может продолжать выполнение кода, даже если он должен быть заблокирован в ожидании завершения setVisible. Вот полный код, который я использовал для его тестирования.
public class Example {
static Thread edt;
static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
static void checkThread(String source) {
Thread thisThread = Thread.currentThread();
String threadCheck = thisThread == edt ? "same thread" : "different thread";
System.out.println(source + " is running on " + thisThread + " (" + threadCheck + ")");
}
public static void main(String[] args) throws Exception {
EventQueue.invokeAndWait(() -> edt = Thread.currentThread());
executor.scheduleAtFixedRate(() -> EventQueue.invokeLater(() -> checkThread("Timer")), 1, 1, TimeUnit.SECONDS);
EventQueue.invokeLater(() -> {
checkThread("File picker");
FileDialog dialog = new FileDialog((Frame) null);
dialog.setVisible(true);
System.out.println("You picked " + dialog.getFile());
checkThread("File picker");
executor.shutdownNow();
});
}
}
checkThread, которая распечатает имя потока и сравнит его со ссылкой, которую я получил ранее.checkThread.Мой вывод выглядит примерно так:
File picker is running on Thread[AWT-EventQueue-0,6,main] (same thread)
Timer is running on Thread[AWT-EventQueue-0,6,main] (same thread)
Timer is running on Thread[AWT-EventQueue-0,6,main] (same thread)
Timer is running on Thread[AWT-EventQueue-0,6,main] (same thread)
You picked somefile.txt
File picker is running on Thread[AWT-EventQueue-0,6,main] (same thread)
Из этого вывода я вижу, что все мои события выполнялись в одном и том же «потоке». Хотя setVisible был заблокирован, три других мероприятия, по-видимому, все еще могли проводиться. Я даже проверил ссылочное равенство на случай, если toString() мне врал. Поэтому я теперь пишу «тема» в кавычках, потому что, основываясь на этом результате, я вообще не могу поверить, что это действительно тема.
Как этот «поток» может продолжать выполнение кода, пока он заблокирован? Ни один другой поток, который я когда-либо видел, на Java или любом другом языке, не может сделать это. Конечно, мы могли бы добиться чего-то подобного с виртуальными потоками, но даже в этом случае каждый отдельный виртуальный поток по-прежнему действует по правилам и прекращает работу, когда его блокируют.
Копая, я наткнулся на класс EventDispatchThread, расширяющий Thread. Правильно ли было бы предположить, что EventDispatchThread — это вообще не настоящая тема?
Моё предположение таково:
EventDispatchThread должен поддерживаться более чем одним реальным потоком, поскольку он может продолжать выполнять события, пока одно событие заблокировано.Thread.sleep() предотвращает отправку других событий.Признаюсь, я немного удивлен этими двумя последними пунктами, поскольку предполагал, что весь смысл однопоточного пользовательского интерфейса заключается в обеспечении потокобезопасности и избежание каких-либо сложных вещей с синхронизацией.
Буду признателен за любые ссылки или документацию, подтверждающие или опровергающие это предположение. Бонусные баллы за то, что вы прольете свет на причину этого.
Потоки — довольно фундаментальная часть языка, и я рассчитываю на их предсказуемое поведение. Как эта тема может быть одновременно заблокирована и не заблокирована? Или, если это не поток, то что он делает, притворяясь им?
См. EventQueue.createSecondaryLoop(). Диалоги AWT могут использовать или не использовать этот класс напрямую; если нет, то они, вероятно, используют что-то эквивалентное.




Ни одно из ваших предположений не верно. Если вы вызываете «пожалуйста, откройте мне диалоговое окно файла» из EDT, затем запускается код, который рисует диалоговое окно файла (и в середине этого процесса ваше приложение не отвечает. Это не имеет значения; его можно быстро нарисовать). Затем, когда приходит время дождаться взаимодействия пользователя с ним, он просто делает что-то вроде:
while (true) {
UiEvent event = UiQueue.fetchNext();
if (event.source == myself) break;
event.handle();
}
// handle the event
где EDT обычно выполняет этот цикл while без части if. Я, конечно, несколько упростил ситуацию.
Обычно этот цикл обработки EDT возникает, когда ваш код, размещенный в EDT, возвращается. Однако диалоговое окно открытия файла также может «хостировать» очередь обработки EDT, закрываясь при возникновении события, что означает, что действие диалогового окна открытия файла готово завершиться тем или иным способом (например, событие в очереди EDT является либо «Отмена» или кнопку «Открыть» в диалоговом окне этого файла).
Конечно! Я слишком долго ломал голову над этим и не могу поверить, что не подумал об этом объяснении. Это имеет смысл.
Я добавляю это как комментарий, а не ответ, потому что я не уверен, но я думаю, что модальное окно перехватывает и обрабатывает события пользовательского интерфейса, поэтому кажется, что остальная часть программы «ждет», хотя она все еще может обрабатывать фоновые операции с использованием того же пула потоков.