Вызов thr.wait()
будет делать следующее:
- Приостановить
Thread
, который вызвал метод! - Освободить любые блокировки
Thread
(который вызвал метод ) в настоящее время выполняется.
Соответствующий вызов метода notify
(или notifyAll
) должен выполняться для того же объекта (ie thr.notify()
или thr.notifyAll()
), который приостановил Thread
мы хотим продолжить.
Обратите внимание, что метод прослушивателя действий actionPerformed
вызывается в потоке отправки событий (сокращенно EDT) (который сам по себе является Thread
). То есть, нажав кнопку end
, вызывается actionPerformed
на EDT, а затем вы вызываете thr.wait()
на нем, что означает, что вы приостанавливаете EDT! В Swing, насколько мне известно, почти каждая операция, связанная с событием, выполняется на EDT. Это означает, что если вы работаете на EDT, вы блокируете другие операции, такие как получение событий от щелчков кнопок, движения мыши и зависания и т. Д. c ... Короче говоря, блокировка EDT означает отсутствие ответа GUI.
Помимо этого, вызов thr.wait()
(а также thr.notify()
и thr.notifyAll()
) должен выполняться внутри блока synchronized (thr) { ... }
.
Если вы хотите взаимодействовать с Thread
другим чем EDT (например, с помощью конструкторов Thread
, ExecutorService
, SwingWorker
и c ...), а также установить связь между двумя Thread
s, обычно вам нужны какие-то синхронизации (потому что у вас есть два Thread
s: EDT и созданный). Вам понадобится эта синхронизация, потому что два Thread
s (для связи) будут совместно использовать [ссылку на] одну и ту же переменную. В вашем случае это флаг print
, которым нужно поделиться; один Thread
(EDT) должен изменять флаг в зависимости от того, какая кнопка была нажата, а другой Thread
(созданный с экземпляром класса Example
, который является Runnable
) с именем thr
, должен повторно прочитать флаг через некоторый интервал / время, а затем выполнить работу по печати в System.out
.
Обратите внимание, что флаг print
является статическим c свойством класса Example
, но вам нужен экземпляр класса для Thread
s для синхронизации. Похоже, вы собирались использовать для этого экземпляр класса Example
с именем thr
.
Возьмем, к примеру, следующий код:
import javax.swing.ButtonGroup;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.SwingUtilities;
public class ThreadMain {
private static class PrintingThread extends Thread {
private boolean print;
public PrintingThread() {
print = false;
}
public synchronized void keepPrinting() {
print = true;
notifyAll();
}
public synchronized void pausePrinting() {
print = false;
}
@Override
public void run() {
try {
while (true) { //You should add an end condition here, in order to let the Thread shutdown gracefully (other than interrupting it).
synchronized (this) {
if (!print)
wait();
}
System.out.println("Printing...");
Thread.sleep(500);
}
}
catch (final InterruptedException ix) {
System.out.println("Printing interrupted.");
}
}
}
private static void createAndShowGUI() {
final PrintingThread printingThread = new PrintingThread();
printingThread.start();
final JRadioButton start = new JRadioButton("Print"),
stop = new JRadioButton("Pause", true);
start.addActionListener(e -> printingThread.keepPrinting());
stop.addActionListener(e -> printingThread.pausePrinting());
/*Creating a button group and adding the two JRadioButtons, means that when
you select the one of them, the other is going to be unselected automatically.
The ButtonGroup instance is then going to be maintained in the model of each
one of the buttons (JRadioButtons) that belong to the group, so you don't need
to keep a reference to group explicitly in case you worry it will get Garbadge
Collected, because it won't.*/
final ButtonGroup group = new ButtonGroup();
group.add(start);
group.add(stop);
final JPanel contentsPanel = new JPanel(); //FlowLayout by default.
contentsPanel.add(start);
contentsPanel.add(stop);
final JFrame frame = new JFrame("Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(contentsPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(final String[] args) {
//EDT related code should be called on the EDT..
SwingUtilities.invokeLater(ThreadMain::createAndShowGUI);
}
}
Здесь вы видите, что я создал собственный метод Thread
и переопределил метод run
для повторной печати на System.out
через некоторый интервал / время в 500 мс. L oop никогда не закончится, если Thread
не будет прервано. Не следует использовать в качестве хорошего примера реализации того, что вы пытаетесь, потому что:
- У него нет условия для нормального завершения
Thread
. Например, у него должно быть условие вместо true
в while
l oop, чтобы указать, когда нам необходимо корректно выйти из Thread
. - Он вызывает
Thread.sleep
в l oop. Насколько я знаю, это считается плохой практикой, потому что обычно это происходит, когда вам нужно выполнять операцию несколько раз и полагаться на Thread.sleep
, чтобы дать вам немного свободного времени, тогда как вместо этого вы должны были использовать ScheduledExecutorService
или java.util.Timer
, чтобы запланировать желаемую операцию с фиксированной скоростью.
Также обратите внимание, что здесь вам нужна синхронизация, потому что у вас есть два Thread
s (EDT и PrintingThread
). Я говорю это еще раз, потому что в следующем примере мы собираемся просто использовать сам EDT для печати (потому что печать в System.out
отдельного сообщения в этом случае не будет слишком длинным), что является еще одним примером реализация того, что вы пытаетесь сделать. Чтобы запланировать операцию с фиксированной скоростью на самом EDT, мы собираемся использовать javax.swing.Timer
, который существует для этой цели.
Код:
import javax.swing.ButtonGroup;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class TimerMain {
private static void createAndShowGUI() {
//Constructs a Timer such that, when running, every 500ms prints the desired message:
final Timer printingTimer = new Timer(500, e -> System.out.println("Printing..."));
/*The Timer is going to repeat events (ie call all its
ActionListeners repeatedly)... This will simulate a loop.*/
printingTimer.setRepeats(true);
/*Coalescing means that events fast enough are going to be merged to one
event only, and we don't want that in this case, so we set it to false:*/
printingTimer.setCoalesce(false);
final JRadioButton start = new JRadioButton("Print"),
stop = new JRadioButton("Pause", true);
start.addActionListener(e -> printingTimer.restart());
stop.addActionListener(e -> printingTimer.stop());
/*Creating a button group and adding the two JRadioButtons, means that when
you select the one of them, the other is going to be unselected automatically.
The ButtonGroup instance is then going to be maintained in the model of each
one of the buttons (JRadioButtons) that belong to the group, so you don't need
to keep a reference to group explicitly in case you worry it will get Garbadge
Collected, because it won't.*/
final ButtonGroup group = new ButtonGroup();
group.add(start);
group.add(stop);
final JPanel contentsPanel = new JPanel(); //FlowLayout by default.
contentsPanel.add(start);
contentsPanel.add(stop);
final JFrame frame = new JFrame("Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(contentsPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(final String[] args) {
//EDT related code should be called on the EDT...
SwingUtilities.invokeLater(TimerMain::createAndShowGUI);
}
}
javax.swing.Timer
делегирует назначение l oop.
Также обратите внимание, здесь мы не использовали ключевое слово synchornized
, потому что нам это не нужно, потому что весь код выполняется на EDT.
SwingUtilities.invokeLater
- это всего лишь несколько методов для вызова Runnable
в EDT в какой-то момент в будущем. Таким образом, нам также необходимо вызвать создание JFrame
, JPanel
и JRadioButton
(или просто вызвать createAndShowGUI
) в EDT, потому что это код, связанный с EDT (например, что, если событие был запущен при добавлении панели к фрейму? ...).
Я добавил несколько комментариев в код, чтобы помочь другим вещам, связанным с показанными примерами.
Дайте мне знать в комментируйте любые вопросы, которые могут возникнуть, и я обновлю свой ответ как можно скорее.