Java Swing : How do I wake up the main thread from the event-dispatch thread?

标签 java multithreading swing events event-dispatch-thread

我想让“主线程”(运行 main() 的线程启动)从按钮的 ActionListener 的 actionPerformed() 方法做一些工作, 但我不知道如何实现这一目标。

更多背景信息:

我目前正在使用 Swing(俄罗斯方 block 的一种风格)编写 2D 游戏。
当应用程序启动时,会打开一个显示游戏主菜单的窗口。 向用户提供了几种可能性,其中一种是通过按下“开始”按钮开始游戏,这会导致显示游戏面板并触发游戏的主循环。

为了能够在两个面板(主菜单面板和游戏面板)之间切换,我使用了 CardLayout 管理器,然后我可以通过调用 show() 显示一个面板。 .
我的想法是,我希望我的开始按钮有一个如下所示的监听器:

public class StartListener implements ActionListener {
    StartListener() {}
    public void actionPerformed(ActionEvent e) {
        displayGamePanel();
        startGame();
    }
}

但这不起作用,因为 actionPerformed() 是从事件派发线程调用的,因此调用 startGame()(触发主循环:游戏逻辑更新 + repaint() 在每一帧调用)阻塞整个线程。

我现在处理这个的方式是 actionPerformed() 只是改变一个 boolean 标志值:public void actionPerformed(ActionEvent e) { 开始推送=真; }
然后最终由主线程检查:

while (true) {
    while (!g.startPushed) {
        try { 
            Thread.sleep(100); 
        } catch (Exception e) {}
    }
    g.startPushed = false;
    g.startGame();
}

但我发现这个解决方案非常不优雅。

我已阅读Concurrency in Swing教训,但我仍然感到困惑(我应该实现一个工作线程——这不是有点矫枉过正吗?)。我还没有完成任何实际的多线程工作,所以我有点迷茫。

有没有办法告诉主线程(它会无限期地 hibernate ,等待用户操作)“好的,现在醒来并执行此操作(显示游戏面板并开始游戏)”?。

感谢您的帮助。

编辑: 需要说明的是,这就是我的游戏循环的样子:

long lastLoopTime = System.currentTimeMillis();
long dTime;
int delay = 10;
while (running) {
    // compute the time that has gone since the last frame
    dTime = System.currentTimeMillis() - lastLoopTime;
lastLoopTime = System.currentTimeMillis();

    // UPDATE STATE
    updateState(dTime);
    //...

    // UPDATE GRAPHICS
    // thread-safe: repaint() will run on the EDT
    frame.repaint()

    // Pause for a bit
    try { 
    Thread.sleep(delay); 
    } catch (Exception e) {}
}

最佳答案

这没有意义:

but this does not work because actionPerformed() is called from the event-dispatch thread, so the call to startGame() (which triggers the main loop: game logic update + repaint() call at each frame) blocks the whole thread.

因为您的游戏循环不应该阻止 EDT。您在游戏循环中使用 Swing Timer 或后台线程吗?如果没有,请这样做。

关于:

while (true) {
    while (!g.startPushed) {
        try { 
            Thread.sleep(100); 
        } catch (Exception e) {}
    }
    g.startPushed = false;
    g.startGame();
}

也不要这样做,而是为这类事情使用监听器。

例如,

import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;

public class GameState extends JPanel {
   private CardLayout cardlayout = new CardLayout();
   private GamePanel gamePanel = new GamePanel();
   private StartPanel startpanel = new StartPanel(this, gamePanel);

   public GameState() {
      setLayout(cardlayout);
      add(startpanel, StartPanel.DISPLAY_STRING);
      add(gamePanel, GamePanel.DISPLAY_STRING);
   }

   public void showComponent(String displayString) {
      cardlayout.show(this, displayString);
   }

   private static void createAndShowGui() {
      GameState mainPanel = new GameState();

      JFrame frame = new JFrame("GameState");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

class StartPanel extends JPanel {
   public static final String DISPLAY_STRING = "Start Panel";

   public StartPanel(final GameState gameState, final GamePanel gamePanel) {
      add(new JButton(new AbstractAction("Start") {

         @Override
         public void actionPerformed(ActionEvent e) {
            gameState.showComponent(GamePanel.DISPLAY_STRING);
            gamePanel.startAnimation();
         }
      }));
   }
}

class GamePanel extends JPanel {
   public static final String DISPLAY_STRING = "Game Panel";
   private static final int PREF_W = 500;
   private static final int PREF_H = 400;
   private static final int RECT_WIDTH = 10;
   private int x;
   private int y;

   public void startAnimation() {
      x = 0;
      y = 0;
      int timerDelay = 10;
      new Timer(timerDelay , new ActionListener() {

         @Override
         public void actionPerformed(ActionEvent e) {
            x++;
            y++;
            repaint();
         }
      }).start();
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      g.fillRect(x, y, RECT_WIDTH, RECT_WIDTH);
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(PREF_W, PREF_H);
   }
}

关于Java Swing : How do I wake up the main thread from the event-dispatch thread?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10263430/

相关文章:

java - 将输入流转换为位图

java - 线程的方法调用未完成 - 如何结束线程 - 解决方法

Java Spring Boot 计划作业

java - iframe 不显示 struts 操作类中的对象

java - 使用 Jackson 序列化为 JSON 时如何在 Java Map 中转换 Java List

java - 每次更新时移动 JLabel 的位置

java - Eclipse 风格的选项卡式 Pane ——选项卡太多时的 "Show List"按钮

java - 链表中 toString() 方法的实现

vb.net - 如何在后台线程中运行代码并仍然访问UI?

java - 更新 MySQL : java. sql.SQLException:没有为参数 8 指定值