我如何创建另一个线程,以便我的 ActionListener 在我的星球模拟运行时仍然监听按钮按下事件?

How do I create another thread so that my ActionListener still listens for button press events while my planet simulation runs?

我有一个 JButton 并向其添加了一个 ActionListener。我还有一个扩展 JPanelSimulationPanel class 并有一个名为 simulationPanel 的实例。两者都添加到 JFrame。在 ActionListeneractionPerformed 方法中,我调用 simulationPanel.startSimulation().

class StartButtonListener implements ActionListener{
    @Override
    public void actionPerformed(ActionEvent arg0) {
        if(mode == MODE_DEFAULT){
            mode = MODE_SIMULATING;
            startButton.setText("Stop Simulation");
            simulationPanel.startSimulation();                              
        } else if(mode == MODE_SIMULATING){
            mode = MODE_DEFAULT;
            startButton.setText("Start Simulation");
            simulationPanel.stopSimulation();
        }
    }
}

stopSimulation() 方法设置 运行 false。

public void startSimulation() {
    running = true;
    long time = System.currentTimeMillis();
    long time2 = System.currentTimeMillis();
    while(running){
        time = System.currentTimeMillis();
        if(time - time2 > 1000/60){
            for(int i = 0; i < planets.size(); i++) {
                planets.get(i).setLocation(planets.get(i).getNewLocation());
            }
            this.paintImmediately(0, 0, 950, 680);
            time2 = System.currentTimeMillis();
        }
    }
}

这会设置我的行星的新位置并每 60 秒重新绘制一次屏幕。

问题在于,一旦进入 while(运行) 循环,ActionListener 不再响应按下的按钮,因此无法调用 stopSimulation()。我将如何创建一个新线程,使按钮能够在模拟为 运行 时响应?

The entire program

当然可以,因为您的启动模拟正在阻塞您的主线程。

我建议你使用 Swing Timer http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html

Swing是单线程环境,如果你阻塞了Event Dispatching Thread,那么它就无法处理Event Queue,这涉及处理用户输入和获取事件。

当您需要执行较长的 运行ning 任务时,您应该将任务卸载到另一个线程。

请记住,Swing 也不是线程安全的,这意味着您永远不应在事件调度线程的上下文之外创建或修改 UI。

在这方面,您可以使用一些工具让您的生活更轻松。

您可以使用 SwingWorker,这将允许您在后台 运行 长 running/blocking 任务,但使用 publish/process 发送更新到 UI 的方法,这不违反 Swing

的单线程规则

您可以使用 Swing Timer,它允许您安排在 EDT 上下文中执行的定期回调,从而可以安全地从

中更新 UI ]

您可以使用普通的旧 Thread 并使用 SwingUtilities.invokeLater 与 EDT 同步更新,但我不喜欢这种方法,因为通过[传递状态信息很痛苦] =16=]