在不冻结 Swing GUI 的情况下使用 Java wait() 和 notify()

Using Java wait() and notify() without freezing Swing GUI

我知道 Swing GUI 本身使用线程,但我正在尝试使用单独的线程来 运行 我的模拟。我创建了一个 class 来实现 Runnable 并像大多数简单的 Runnable 示例一样使用自定义线程。我的 运行() 方法基本上是 运行 我的模拟,每秒更新一次(效果很好),但我现在正在尝试实现可以​​ pause/resume 模拟的按钮。我的开始按钮成功启动线程,我的暂停按钮成功暂停线程。但是,当 selected 暂停时,整个 GUI 暂停,您可以看到按钮仍在 selected 并且我无法 select 任何按钮或与 GUI 交互根本。一旦我在我的自定义线程上调用 wait() ,我的整个 GUI 就会停止,即使我使用的是与该 GUI 分开的线程。 为什么调用 wait() 会冻结我的 GUI?我怎样才能暂停这个特定的线程而不是整个 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();
    }

}

Thread 对象上调用 waitnotify 的行为与在任何其他对象上的行为没有区别。具体来说,正如您所注意到的,它不会向正在执行的线程发送应该暂停的信号,而是会阻塞调用线程(在本例中为您的 UI 线程),直到它收到 notify 消息.

您需要实现从 UI 线程到后台线程的消息传递系统(例如阻塞队列),以便获得您想要的内容。