Я понимаю, что сами Swing GUI используют Threads, но я пытаюсь использовать отдельный поток для запуска моделирования. Я создал класс, который реализует Runnable и использует пользовательский поток, как это делают самые простые примеры Runnable. Мой метод run() в основном запускает мое моделирование, обновляя каждую секунду (что отлично работает), но теперь я пытаюсь реализовать кнопки, которые могут приостановить/возобновить симуляцию. Моя кнопка «Пуск» успешно запускает поток, и моя кнопка «Пауза» успешно приостанавливает поток. Однако, когда выбрана пауза, весь графический интерфейс приостанавливается, и вы можете видеть, что кнопка по-прежнему выбрана, и я не могу выбрать какие-либо кнопки или вообще взаимодействовать с графическим интерфейсом. Как только я вызываю wait() в свой пользовательский поток, весь мой GUI останавливается, хотя я использую Thread отдельно от этого графического интерфейса. Почему вызов wait() замерзает мой графический интерфейс? Как я могу приостановить только этот конкретный поток, а не весь графический интерфейс?Использование Java wait() и notify() без замораживания Swing GUI
Обратите внимание: кнопка запуска должна быть возобновлена. Вот мой код для GUI:
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GridFrame extends JFrame {
private static final long serialVersionUID = 2857470112009359285L;
private MyGridPanel grid;
private Simulation sim;
GridFrame(Simulation sim, int w, int h, int rows, int cols) {
this.sim = sim;
setTitle("Simulation");
setSize(w, h);
grid = new MyGridPanel(w, h, rows, cols);
add(grid, BorderLayout.CENTER);
//Build bottom panel
JPanel buttons = new JPanel();
buttons.setLayout(new GridLayout(1,3));
JButton start = new JButton("Start");
JButton pause = new JButton("Pause");
JButton reset = new JButton("Reset");
start.setActionCommand("Start");
start.addActionListener(new ButtonActionListener());
pause.setActionCommand("Pause");
pause.addActionListener(new ButtonActionListener());
reset.setActionCommand("Reset");
reset.addActionListener(new ButtonActionListener());
buttons.add(start);
buttons.add(pause);
buttons.add(reset);
add(buttons, BorderLayout.SOUTH);
}
public MyGridPanel getGrid(){
return grid;
}
private class ButtonActionListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
switch(e.getActionCommand()){
case "Start":
System.out.println("Start");
sim.start();
break;
case "Pause":
System.out.println("Pause");
sim.pause();
break;
case "Reset":
System.out.println("Reset");
break;
}
}
}
}
А вот мой Runnable:
public class Simulation implements Runnable{
private Thread t;
private GridFrame frame;
private boolean paused;
public Simulation(){
frame = new GridFrame(this, 300, 300, 10, 10);
frame.setVisible(true);
paused = true;
}
public void start() {
if(t == null){
//Thread has not been created. Simulation has not started to run yet
System.out.println("Starting thread.");
t = new Thread(this);
t.start();
paused = false;
}
else if(paused){
//Simulation and thread already started to run but was paused. This should use notify() to resume the thread.
resume();
paused = false;
}
}
public void resume(){
synchronized(t){
t.notify();
}
}
public void pause(){
synchronized(t){
try {
t.wait();
paused = true;
} catch (InterruptedException e) {
System.out.println("Exception when trying to pause simulation");
e.printStackTrace();
}
}
}
@Override
public void run() {
while(true){
frame.getGrid().step();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("Thread interrupted while simulation was running.");
e.printStackTrace();
}
}
}
public static void main(String[] a) {
Simulation s = new Simulation();
}
}
(1+) приятный ответ. –
Хороший ответ, за исключением того, что «вызывающий поток» является более подходящим описанием, чем «текущий исполняемый» поток. –
Достаточно честно. Отредактировано соответственно. –