Вопрос немного широкий и неопределенный, но я постараюсь ответить на некоторые из вопросов, которые вы задали.Отправной точкой для дальнейших собственных исследований, вероятно, является Урок: параллелизм в Swing , хотя из этого действительно может быть сложно получить определенные утверждения для конкретных случаев.
Прежде всего, естьявляется всеобъемлющим правилом в Swing, которое называется Правило однопотоковой обработки :
После реализации компонента Swing весь код, который может влиять или зависеть от состояния этогокомпонент должен выполняться в потоке диспетчеризации событий.
(К сожалению, в учебнике об этом больше не говорится так четко)
Помня об этом, глядя на ваши фрагменты:
// OUTSIDE EDT
JFrame mainFrame = new JFrame("Java SWING Examples");
...
Это часто так, к сожалению - и, к сожалению, даже в некоторых официальных примерах Swing.Но это уже может вызвать проблемы.Чтобы быть в безопасности, GUI (включая основную раму) всегда должен обрабатываться на EDT, используя SwingUtilities#invokeLater
.Тогда шаблон всегда один и тот же:
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
private static void createAndShowGui() {
JFrame mainFrame = new JFrame("Java SWING Examples");
...
mainFrame.setVisible(true);
}
Что касается второго примера, который вы показали, с классом LabelUpdater
: мне было бы любопытно, из какой статьи вы это получили.Я знаю, что существует много cr4p, но этот пример даже отдаленно не имеет смысла ...
public class LabelUpdater implements Runnable {
private JLabel statusLabel;
...
@Override
public void run() {
...
statusLabel.setText("Ok Button clicked.");
}
}
Если этот код (то есть метод run
) выполняется в новом потоке, то это явно нарушает правило single thread : статус JLabel
изменяется из потока, который является не потоком отправки события!
Основным моментом запуска нового потока в обработчике событий (например, в методе actionPerformed
для ActionListener
) является предотвращение блокировки пользовательского интерфейса .Если бы у вас был какой-то код, подобный этому
someButton.addActionListener(e -> {
doSomeComputationThatTakesFiveMinutes();
someLabel.setText("Finished");
});
, то нажатие кнопки привело бы к блокировке EDT на 5 минут - т.е. графический интерфейс пользователя "завис бы" и выглядел бы так, как будто он завис.В этих случаях (т. Е. Когда у вас длительные вычисления) вы должны выполнять работу в собственном потоке.
Наивный подход к выполнению этого вручную может (примерно) выглядеть следующим образом:
someButton.addActionListener(e -> {
startBackgroundThread();
});
private void startBackgroundThread() {
Thread thread = new Thread(() -> {
doSomeComputationThatTakesFiveMinutes();
someLabel.setText("Finished"); // WARNING - see notes below!
});
thread.start();
}
Теперь нажатие кнопки запускает новый поток, и графический интерфейс больше не блокируется.Но обратите внимание на WARNING
в коде: теперь снова возникает проблема изменения JLabel
потоком, который является не потоком отправки события!Так что вам придется передать это обратно в EDT:
private void startBackgroundThread() {
Thread thread = new Thread(() -> {
doSomeComputationThatTakesFiveMinutes();
// Do this on the EDT again...
SwingUtilities.invokeLater(() -> {
someLabel.setText("Finished");
});
});
thread.start();
}
Это может выглядеть неуклюже и сложно, и, как если бы вам было трудно выяснить, какой поток вы в настоящее время находитесь.И это правильно.Но для обычной задачи запуска длительной задачи есть класс SwingWorker
, объясненный в учебном пособии , который делает этот шаблон несколько проще.
Бесстыдная самореклама: Некоторое время назад я создал библиотеку SwingTasks
, которая по сути является "Swing Worker на стероидах".Он позволяет вам «связывать» методы, подобные этим ...
SwingTaskExecutors.create(
() -> computeTheResult(),
result -> receiveTheResult(result)
).build().execute();
, и обеспечивает отображение (модального) диалога, если выполнение занимает слишком много времени, и предлагает некоторые другие удобные методы, например, для отображенияиндикатор выполнения в диалоге и так далее.Образцы суммированы в https://github.com/javagl/SwingTasks/tree/master/src/test/java/de/javagl/swing/tasks/samples