title: Why actionPerformed is executed by two Threads?
body: <p>I'm creating a simple game with swing. I'm trying to handle input with <code>ActionMap</code> and <code>InputMap</code>, therefore I'm using an <code>Action</code> to do the thing I need to do, in this particular case switch to the main menu and resetting the game panel. Now, I've noticed that the function <code>showMenu()</code> is called multiple times by two Threads and the key is being pressed only one time. Why is that so? And how can I prevent it?</p>
  <p>To reproduce the error simply start the main in <code>Startup.java</code> click play and then press the <code>m</code> key on your keyboard; that should reproduce in the console an output similar to mine.</p>
  <p>There is the output, with the threads' names, that I get:</p>
  <pre class="lang-text prettyprint-override"><code>I'M PRESSING M - THREAD: AWT-EventQueue-0
  CALLING showMenu OF GUIHANLDER - THREAD AWT-EventQueue-0      
  I am in stop - GameController - THREAD:AWT-EventQueue-0       
  Start of reset Game - GameController - THREAD:AWT-EventQueue-0
  CALLING showMenu OF GUIHANLDER - THREAD Thread-0
  I am in stop - GameController - THREAD:Thread-0
  Start of reset Game - GameController - THREAD:Thread-0
  Created a player X: 24 Y:40
  resetted
  Created a player X: 49 Y:5
  End of reset Game - GameController - THREAD:AWT-EventQueue-0
  resetted
  End of reset Game - GameController - THREAD:Thread-0
  Called resetGame() - GameController - THREAD:AWT-EventQueue-0
  Called resetGame() - GameController - THREAD:Thread-0
  pressed M - THREAD: AWT-EventQueue-0
  </code></pre>
  <p>Those are the main interested areas:</p>
  <p>GamePanel.java</p>
  <pre class="lang-java prettyprint-override"><code>import java.awt.Color;
  import java.awt.event.ActionEvent;
  import java.util.Random;
  
  import javax.swing.AbstractAction;
  import javax.swing.Action;
  import javax.swing.ActionMap;
  import javax.swing.InputMap;
  import javax.swing.JPanel;
  import javax.swing.KeyStroke;
  public class GamePanel extends JPanel {
  
      public static final String PANEL_ID = &quot;game_panel&quot;;
  
      public static final int DEFAULT_WIDTH = 100;
      public static final int DEFAULT_HEIGHT = 100;
  
      private int mapWidth = -1;
      private int mapHeight = -1;
      private Random r = new Random();
  
      public GamePanel() {
          setBackground(Color.BLACK);
          createPlayer();
          initSetup();
      }
  
      public void createPlayer() {
          int x = r.nextInt(100);
          int y = r.nextInt(100);
          System.out.println(&quot;Created a player X: &quot; + x + &quot; Y:&quot; + y);
      }
  
      public void initSetup() {
          repaint();
          initInput();
      }
  
      private void initInput() {
          InputMap iMap = getInputMap();
          ActionMap aMap = getActionMap();
  
          Action goToMenu = new AbstractAction() {
              @Override
              public void actionPerformed(ActionEvent e) {
                  System.out.println(&quot;I'M PRESSING M - THREAD: &quot;+Thread.currentThread().getName());
                  GUIHandler.getInstance().showMenu();
                  System.out.println(&quot;pressed M - THREAD: &quot;+Thread.currentThread().getName());
              }
          };
          aMap.put(&quot;goToMenu&quot;, goToMenu);
          iMap.put(KeyStroke.getKeyStroke('m'), &quot;goToMenu&quot;);
      }
  
      public void reset() {
          if (mapHeight == -1 || mapWidth == -1) {
              mapWidth = DEFAULT_WIDTH;
              mapHeight = DEFAULT_HEIGHT;
          }
          createPlayer();
          initSetup();
          System.out.println(&quot;resetted&quot;);
      }
  
  }
  
  </code></pre>
  <p>GameController.java</p>
  <pre class="lang-java prettyprint-override"><code>public class GameController {
  
      private GamePanel gamePanel;
      private GameLoop gameLoop;
  
      private static GameController instance = null;
  
      private GameController() {
          gamePanel = GUIHandler.getInstance().getGamePanel();
  
          gameLoop = new GameLoop();
      }
  
      public void start() {
          if (!gameLoop.isGameRunning())
              gameLoop.run();
      }
  
      public void stop() {
          System.out.println(&quot;I am in stop - GameController - THREAD:&quot;+Thread.currentThread().getName());
          if (gameLoop.isGameRunning())
              gameLoop.stop();
          resetGame();
          System.out.println(&quot;Called resetGame() - GameController - THREAD:&quot;+Thread.currentThread().getName());
      }
  
      private void resetGame() {
          System.out.println(&quot;Start of reset Game - GameController - THREAD:&quot;+Thread.currentThread().getName());
          gamePanel.reset();
          System.out.println(&quot;End of reset Game - GameController - THREAD:&quot;+Thread.currentThread().getName());
      }
  
      public GamePanel getGamePanel() {
          return gamePanel;
      }
  
      public static GameController getInstance() {
          if (instance == null)
              instance = new GameController();
          return instance;
      }
  
  }
  
  </code></pre>
  <p>GameLoop.java</p>
  <pre class="lang-java prettyprint-override"><code>
  public class GameLoop {
  
      private static final String STOPPED = &quot;stopped&quot;;
      private static final String RUNNING = &quot;running&quot;;
      private static final int FPS_GOAL = 60;
      private static final int MS_PER_FRAME = 1000/FPS_GOAL;
      
      protected String status;
      private Thread gameThread;
  
      protected GameLoop() {
          status = STOPPED;
      }
  
      public void run() {
          status = RUNNING;
          gameThread = new Thread(this::processGameLoop);
          gameThread.start();
      }
  
      public void stop() {
          status = STOPPED;
          gameThread.interrupt();
          gameThread = null;
      }
  
      public boolean isGameRunning() {
          return status == RUNNING;
      }
  
      private void render()   {
          GameController.getInstance().getGamePanel().repaint();
      }
  
      protected void processGameLoop()    {
          while (isGameRunning())
          {
              long start = System.currentTimeMillis();
              render();
  
              try {
                  long tiemout = start + MS_PER_FRAME - System.currentTimeMillis();
                  if (tiemout &gt; 0)
                      Thread.sleep(tiemout);
              } catch (InterruptedException e) {
                  Thread.currentThread().interrupt();
                  GUIHandler.getInstance().showMenu();
              }
          }
  
      }
  }
  </code></pre>
  <p>GUIHandler.java</p>
  <pre class="lang-java prettyprint-override"><code>import javax.swing.BorderFactory;
  import javax.swing.JFrame;
  import javax.swing.JPanel;
  
  import java.awt.BorderLayout;
  import java.awt.CardLayout;
  import java.awt.Dimension;
  public class GUIHandler {
  
      private JPanel contentPane;
      private Menu menu;
      private GamePanel game;
  
      public static final Dimension PREFERRED_DIMENSION = new Dimension(1200, 800);
  
      private static GUIHandler instance = null;
  
      private GUIHandler()    {}
      
      public void displayGUI()    {
          JFrame frame = new JFrame(&quot;Square Territory&quot;);
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  
          contentPane = new JPanel();
          contentPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
          contentPane.setLayout(new CardLayout());
          
          menu = new Menu();
          game = new GamePanel();
  
          contentPane.add(menu, Menu.PANEL_ID);
          contentPane.add(game, GamePanel.PANEL_ID);
  
          frame.setPreferredSize(PREFERRED_DIMENSION);
          frame.getContentPane().add(contentPane, BorderLayout.CENTER);
          frame.pack();
          frame.setLocationByPlatform(true);
          frame.setVisible(true);
      }
  
      public GamePanel getGamePanel() {return game;}
  
      public void startGame() {
          ((CardLayout) contentPane.getLayout()).show(contentPane, GamePanel.PANEL_ID);
          game.grabFocus();
          GameController.getInstance().start();
      }
      public void showMenu()  {
          System.out.println(&quot;CALLING showMenu OF GUIHANLDER - THREAD &quot;+Thread.currentThread().getName());
          GameController.getInstance().stop();
          ((CardLayout) contentPane.getLayout()).show(contentPane, Menu.PANEL_ID);
      }
  
      public static GUIHandler getInstance()  {
          if (instance == null)
              instance = new GUIHandler();
          return instance;
      }
  
  }
  </code></pre>
  <p>Menu.java</p>
  <pre class="lang-java prettyprint-override"><code>import javax.swing.Box;
  import javax.swing.BoxLayout;
  import javax.swing.JButton;
  import javax.swing.JLabel;
  import javax.swing.JPanel;
  import javax.swing.JSpinner;
  import javax.swing.JTextPane;
  import javax.swing.SpinnerModel;
  import javax.swing.SpinnerNumberModel;
  import javax.swing.JSpinner.NumberEditor;
  import javax.swing.event.MouseInputAdapter;
  import javax.swing.text.SimpleAttributeSet;
  import javax.swing.text.StyleConstants;
  
  import java.awt.Color;
  import java.awt.Component;
  import java.awt.Font;
  import java.awt.event.MouseEvent;
  import java.awt.BorderLayout;
  import java.awt.Dimension;
  
  public class Menu extends JPanel {
  
      public static final String PANEL_ID = &quot;menu_panel&quot;;
  
      /**
       * Creating the menu and its components.
       */
      public Menu() {
          setOpaque(true);
          setBackground(Color.CYAN.darker());
          setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
  
          JTextPane logo = new JTextPane();
          logo.setText(&quot;SquareTerritory&quot;);
          logo.setFont(new Font(Font.SANS_SERIF, Font.ITALIC + Font.BOLD, 60));
          SimpleAttributeSet center = new SimpleAttributeSet();
          StyleConstants.setAlignment(center, StyleConstants.ALIGN_CENTER);
          logo.getStyledDocument().setParagraphAttributes(0, logo.getStyledDocument().getLength(), center, false);
          logo.setBackground(Color.CYAN.darker());
          logo.setEditable(false);
          logo.setFocusable(false);
  
          JPanel dimensionChoose = new JPanel(new BorderLayout());
          SpinnerModel widthModel = new SpinnerNumberModel(100, 10, 800, 1);
          JSpinner width = new JSpinner(widthModel);
          SpinnerModel heightModel = new SpinnerNumberModel(100, 10, 800, 1);
          JSpinner height = new JSpinner(heightModel);
          width.getEditor().setPreferredSize(new Dimension(200, 50));
          width.setToolTipText(&quot;Width of the grid&quot;);
          ((NumberEditor) width.getEditor()).getTextField().setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 40));
          ((NumberEditor) width.getEditor()).getTextField().setBackground(Color.CYAN.darker().darker());
  
          height.getEditor().setPreferredSize(new Dimension(200, 50));
          height.setToolTipText(&quot;Height of the grid&quot;);
          ((NumberEditor) height.getEditor()).getTextField().setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 40));
          ((NumberEditor) height.getEditor()).getTextField().setBackground(Color.CYAN.darker().darker());
  
          JPanel labelPane = new JPanel();
          JLabel widthLabel = new JLabel(&quot;Width&quot;);
          widthLabel.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 40));
          JLabel heightLabel = new JLabel(&quot;Height&quot;);
          heightLabel.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 40));
  
          labelPane.setBackground(Color.CYAN.darker());
          labelPane.add(widthLabel);
          labelPane.add(Box.createGlue());
          labelPane.add(heightLabel);
  
          widthLabel.setLabelFor(width);
          heightLabel.setLabelFor(height);
          dimensionChoose.add(width, BorderLayout.WEST);
          dimensionChoose.add(labelPane);
          dimensionChoose.add(height, BorderLayout.EAST);
  
          JButton play = new JButton(&quot;Play&quot;);
          play.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 40));
          play.setForeground(Color.WHITE);
          play.setBackground(Color.DARK_GRAY);
          play.addMouseListener(new MouseInputAdapter() {
              @Override
              public void mouseEntered(MouseEvent e) {
                  play.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 40));
                  play.setBackground(Color.LIGHT_GRAY);
              }
              
              @Override
              public void mouseExited(MouseEvent e) {
                  play.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 40));
                  play.setBackground(Color.DARK_GRAY);
              }
          });
          play.addActionListener(e -&gt; GUIHandler.getInstance().startGame());
  
          JButton exit = new JButton(&quot;Exit&quot;);
          exit.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 40));
          exit.setForeground(Color.WHITE);
          exit.setBackground(Color.DARK_GRAY);
          exit.addMouseListener(new MouseInputAdapter() {
              @Override
              public void mouseEntered(MouseEvent e) {
                  exit.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 40));
                  exit.setBackground(Color.LIGHT_GRAY);
              }
              
              @Override
              public void mouseExited(MouseEvent e) {
                  exit.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 40));
                  exit.setBackground(Color.DARK_GRAY);
              }
          });
          exit.addActionListener(e -&gt; System.exit(0));
  
          logo.setAlignmentX(Component.CENTER_ALIGNMENT);
          logo.setMaximumSize(new Dimension(logo.getMaximumSize().width, 500));
          dimensionChoose.setAlignmentX(Component.CENTER_ALIGNMENT);
          dimensionChoose.setMaximumSize(new Dimension(800, 200));
          dimensionChoose.setBackground(Color.CYAN.darker());
          play.setAlignmentX(Component.CENTER_ALIGNMENT);
          exit.setAlignmentX(Component.CENTER_ALIGNMENT);
  
          add(logo);
          add(Box.createVerticalGlue());
          add(dimensionChoose);
          add(Box.createVerticalGlue());
          add(play);
          add(Box.createVerticalGlue());
          add(exit);
      }
  
  }
  </code></pre>
  <p>Startup.java</p>
  <pre class="lang-java prettyprint-override"><code>public class Startup {
      public static void main(String[] args) {
          GUIHandler gh = GUIHandler.getInstance();
          gh.displayGUI();
      }
  }
  </code></pre>