Сводка
По какой-то причине, когда я вызываю SecondaryLoop.enter()
в потоке диспетчеризации событий AWT (EDT), он не ожидает SecondaryLoop.exit()
длябыть вызванным перед разблокировкой.
Справочная информация
Поскольку я думаю, что SecondaryLoop
не очень известный класс, я дам краткий обзор:
В общем,плохая идея иметь какой-либо долго выполняющийся или блокирующий код, работающий на EDT, потому что тогда ваше приложение не будет реагировать на какие-либо события, пока этот код не завершится. EventQueue.createSecondaryLoop()
позволяет вам создать новый цикл обработки событий, который будет обрабатывать события, позволяя блокировать EDT без потери скорости отклика. Это то, что используются в модальных диалоговых окнах свинга, чтобы позволить вам блокировать EDT, пока вы ждете закрытия диалогового окна, но при этом все элементы управления в самом диалоговом окне могут работать.
После создания SecondaryLoop
Например, вы должны иметь возможность вызывать enter()
, и он должен блокироваться до тех пор, пока не будет вызван exit()
.
Из документов
Этот метод может вызываться любым потоком, включаяпоток рассылки событий. Этот поток будет заблокирован до тех пор, пока не будет вызван метод exit () или цикл не будет завершен. Новый поток будет создан в потоке диспетчеризации событий для диспетчеризации событий в любом случае.
Я не совсем уверен, что это значит, когда он говорит "или цикл завершается", хотя. Это может быть моей проблемой.
Тестовый код
Вызов метода enter()
в потоке, отличном от EDT, блокируется, как и следовало ожидать:
System.out.println("Enter Loop");
Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop().enter();
System.out.println("Done (we should never get here)");
Вывод:
Enter Loop
Однако, если мы вызываем его на EDT, он блокируется примерно на секунду, но затем продолжает:
System.out.println("Enter Loop");
try {
SwingUtilities.invokeAndWait(() -> Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop().enter());
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
System.out.println("Done (we should never get here)");
Вывод:
Enter Loop
Done (we should never get here)
Согласно комментарию tevemadar (спасибо BTW), я обновил код, чтобы предотвратить возможные проблемы со сборкой мусора:
//Storing loop in array as a quick hack to get past the
// "final or effectively final" issue when using this in the invokeAndWait
SecondaryLoop loop[] = new SecondaryLoop[1];
System.out.println("Enter Loop");
try {
SwingUtilities.invokeAndWait(() -> {
loop[0] = Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop();
loop[0].enter();
});
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
System.out.println("Done (we should never get here)");
//Just printing this to make sure that it is used after the invokeAndWait is done. This is just
//to make sure there isn't some sort of optimization thing that is deciding that we don't
//need this anymore and allowing the loop to be garbage collected
System.out.println(loop[0]);
Вывод:
Enter Loop
Done (we should never get here)
java.awt.WaitDispatchSupport@2401f4c3
Итак, покаэто было хорошее предложение, которое, похоже, не является моей проблемой.
Это кажется довольно противоречивым для документации (и для меня цели всего SecondaryLoop
. Я что-то упустил?
Среда
ОС: Windows 10
Java:
C:\Program Files\Java\jre8\bin>java.exe -version
java version "1.8.0_221"
Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)
Обновление
На догадку я попытался добавить таймер, который постоянно добавляет больше событийв цикл EDT. Похоже, что добавление таймера поддерживает цикл и блокирует его:
//Add a keep alive timer which adds an event to the EDT for every 0.5 sec
new Timer(500, null).start();
System.out.println("Enter Loop");
try {
SwingUtilities.invokeAndWait(() -> Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop().enter());
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
System.out.println("Done (we should never get here)");
С этим кодом онзависает, как я ожидал, и если я через некоторое время вставлю в цикл некоторый код, вызывающий метод exit()
, он завершится, как я и ожидал. Таким образом, кажется, что цикл должен завершиться сам, как только он прошел определенное количество времени без события (но только если он был первоначально вызван из EDT по какой-то причине ...).
Я полагаю, что могудобавляйте таймеры, которые ничего не делают, когда мне нужно использовать эту функцию, но это определенно скорее обходной путь, чем исправление, по моему мнению.