Java: графические интерфейсы должны быть инициализированы в потоке EDT?

Я Джейсон. У меня небольшая проблема с внешним видом Substance (https://substance.dev.java.net/).

Моя проблема более общая. Я уже написал свой графический интерфейс, и он работает нормально, но когда я использую Substance Look-and-feel, он требует, чтобы вся инициализация GUI происходила в потоке EDT (поток диспетчеризации событий или что-то в этом роде).

Прямо сейчас я использую com.sun.java.swing.plaf.windows.WindowsLookAndFeel (не уверен, что правильно написал), и ничего подобного не требуется.

Поэтому я поместил основную инициализацию в EDT, вызвав SwingUtilities.invokeLater(). Это заставило его работать. Однако программа также порождает несколько других окон во время своего выполнения. Прямо сейчас у меня есть код вроде:

SomeNewWindow window = new SomeNewWindow();
// ... some bs emitted
window.doStuff();

Этот код работает нормально, потому что к моменту вызова window.doStuff() он уже инициализирован. Но Substance требует от меня сделать что-то вроде этого:

SwingUtilities.invokeLater(new Runnable(){
public void run(){
SomeNewWindow window = new SomeNewWindow();
}});
// ... bs emitted
window.doStuff();

Здесь он иногда генерирует исключение NullPointerException, потому что окно не инициализировано к моменту вызова window.doStuff(). Я не могу поместить window.doStuff() в поток EDT, потому что обычно для возврата требуется несколько секунд, и графический интерфейс зависает.

Я попытался поместить Thread.sleep(1000) сразу после вызова потока EDT, потому что к тому времени он, вероятно, уже инициализирован. Но это кажется неудобным. Мне просто нужен способ, чтобы основной поток «знал», когда возвращается инициализация SomeNewWindow, чтобы он мог продолжать работу, не беспокоясь об исключении NullPointerException.

Заранее спасибо.


person Community    schedule 21.02.2009    source источник


Ответы (4)


Вы можете переключиться с invokeLater на invokeAndWait, который будет ждать, пока окно не будет создано. Это немного дрянно, но не так плохо, как положить в сон.

person Paul Tomblin    schedule 21.02.2009

Я думаю, что стандартный подход к этому будет заключаться в том, чтобы сделать ваш EDT «базовым потоком», из которого вы запускаете другие рабочие потоки для выполнения каких-либо действий.

Другим способом было бы использование флага volatile, который инициализатор может установить, когда это будет сделано, чтобы другой поток мог проверить его в цикле и действовать в новом окне после установки флага.

person Zach Scrivena    schedule 21.02.2009

Egwor предлагает использовать CountDownLatch вместо этого. Определенно похоже, что это упростит ситуацию.


Это задание для условия переменные.

По сути, в run() заблокируйте блокировку, создайте новое окно и сигнализируйте условие (и разблокируйте блокировку). "Тем временем", в другом потоке, делайте свои другие "бс", запирайте замок; если окно имеет значение null, wait() для условной переменной; разблокировать замок; окно.доштуфф();

person Community    schedule 21.02.2009
comment
Я думаю, что защелка обратного отсчета может быть лучше, не так ли? - person Egwor; 22.02.2009
comment
Может быть. Я не знаю, что такое защелка обратного отсчета :) - person Logan Capaldo; 22.02.2009

Есть ли причина, по которой вы не можете просто переместить вызов doStuff() в обратный вызов invokeLater?

SwingUtilities.invokeLater(new Runnable(){
    public void run(){
         SomeNewWindow window = new SomeNewWindow();
         window.doStuff();
    }
});

Если это невозможно, я бы выбрал invokeAndWait() вместо invokeLater(), как уже предложил Пол Томблин.

person Barend    schedule 22.02.2009
comment
И нет, потому что window.doStuff обычно занимает несколько секунд или минут. Я пытаюсь это сделать, и окно на это время становится прозрачным. - person ; 23.02.2009