如何阻止ActionListener暂停所有其他代码?

时间:2014-02-20 05:38:15

标签: java swing awt actionlistener synchronized

昨天我问过this问题并尝试实施我收到的最佳答案。因此,从昨天的代码开始,我尝试将synchronized添加到我的方法并使用wait()notifyAll()。我一直在查看一堆示例,并阅读文档,但我肯定没有做对。

基本上,一旦touchEvent发生,我的ButtonListener就会阻止我的所有其他代码执行,并且只执行ButtonListener中包含的代码。 这只发生在我的一台计算机上,但是我的笔记本电脑按照我预期的方式运行代码,我的桌面卡在ButtonListener这是我转入的学校作业几个星期前,最初收到70%,但我向老师解释说,它可以在某些电脑上运行,并且在办公室桌面上运行,而中提琴,它有效,我得到了100%。当然我想知道问题是什么,所以这就是为什么我还在努力。

以下是有问题的代码段:

    public synchronized void playOneTurn(int player)
        throws InterruptedException {
    waiting = true;
    while (waiting) {
        try {
            System.out.println("playOneTurn before wait.");
            wait();
        } catch (InterruptedException e) {
            // e.printStackTrace();
        }
    }
    System.out.println("playOneTurn AFTER wait.");
    }

我的方法playOneTurn是在第一次触摸事件之前运行的最后一段代码。以前,通过查看我在顶部链接的问题可以看出,我使用了一个简单的while循环,等待我的ButtonListener翻转布尔值waiting。以上是我尝试使用synchronized

    class ButtonListener implements ActionListener {
    @Override
    public synchronized void actionPerformed(ActionEvent e) {
        System.out.println("Entering Action Performed");
        for (i = 0; i < size; i++) {
            for (j = 0; j < size; j++) {
                if (e.getSource() == cells[i][j]) {
                    if (model[i][j] == 'e') {
                        cells[i][j].setText("");
                        currentSpot[0] = i;
                        currentSpot[1] = j;
                        if (count % 2 == 0) {
                            cells[i][j].setBackground(Color.GREEN);
                            cells[i][j].setIcon(X_MARK);
                            model[i][j] = 'x';
                            count++;
                            waiting = false;
                            System.out.println("Boolean hit");
                            notifyAll();
                        } else {
                            cells[i][j].setBackground(Color.CYAN);
                            cells[i][j].setIcon(O_MARK);
                            model[i][j] = 'o';
                            count++;
                            waiting = false;
                            System.out.println("Boolean hit");
                            notifyAll();
                        }

                    } else {
                        System.out
                                .println("Hey, you can't move there silly!");
                    }
                }
            }
        }
    }
    }

这是我的ButtonListener我的程序只是冷静下来并停止做其他任何事情。请随意忽略我的随机println,我只是变得更加恶化并试图找出这件事正在做什么。

我还必须将此try / catch块添加到我的控制器类:

        while (!game.haveWinner() && !game.isFull()) {
        player = (player + 1) % 2;
        try {
            game.playOneTurn(player);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
             e.printStackTrace();
        }
        System.out.println("While loop looped");
    }

我一直在尝试各种方法来正确实施synchronized,但我显然做错了。

Here is a Dropbox link to the entire program if it would help.

3 个答案:

答案 0 :(得分:5)

此:

while (!game.haveWinner() && !game.isFull()) {

将绑定Swing事件线程并冻结整个应用程序....不要这样做。

您的代码看起来像是在尝试将线性控制台程序入侵到Swing GUI中,并且由于程序流程和逻辑完全不同而无法运行。解决方案是将您的逻辑更改为更多事件驱动。

你可能在某个地方有一个游戏循环,也许是使用Swing Timer ...所以检查循环的每次迭代以获胜或者已经满了。

你需要让所有等待,所有同步,所有通知你的程序。而是按下按钮改变它的状态。


修改
我正在玩你的代码,并想出了类似的东西。请注意,同步不是我强大的套件,所以请考虑一下,但我的主要目标是确保仅在Swing事件线程上调用Swing GUI创建代码和状态更改代码,以及任何其他代码,特别是需要与其他线程同步的代码,不会在Swing事件线程上调用。

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Scanner;
import javax.swing.*;

public class TicTacToeApp {
   public static void main(String[] args) {
      TicTacToeGame game;
      int size, need, player = 1;
      String[] names = new String[2];
      Scanner kbd = new Scanner(System.in);

      // TODO: uncomment in running code
      // System.out.print("Enter Player 1's name:  ");
      // names[0] = kbd.nextLine();
      // System.out.print("Enter Player 2's name:  ");
      // names[1] = kbd.nextLine();
      //
      // System.out.print("Enter the TIC-TAC-TOE grid size:  ");
      // size = kbd.nextInt();
      // System.out.print("Enter how many in a row you need to win:  ");
      // need = kbd.nextInt();
      // System.out.println();

      // TODO: For test purposes only. Delete in running code
      size = 3;
      need = 3;
      names[0] = "Foo";
      names[1] = "Bar";

      game = new TicTacToeGame(size, need, names);

      while (!game.haveWinner() && !game.isFull()) {
         player = (player + 1) % 2;
         try {
            game.playOneTurn(player);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
         System.out.println("While loop looped");
      }

      if (game.haveWinner())
         System.out.println(names[player] + " is the winner!");
      else
         System.out.println("It's a TIE!");

      System.out.println("\nBye!");

   }
}

@SuppressWarnings("serial")
class TicTacToeGame extends JPanel {
   private static final Object LOCK = new Object();
   private volatile int player = 0;
   private int size;
   private int need;
   private String[] names;
   private JLabel nameLabel = new JLabel();
   // private JButton testButton = new JButton();
   private JButton[][] buttonGrid;
   private volatile boolean waiting = false;

   public TicTacToeGame(int size, int need, String[] names) {
      this.size = size;
      this.need = need;
      this.names = names;

      nameLabel.setText(names[0]);

      JPanel topPanel = new JPanel();
      topPanel.add(new JLabel("Player:"));
      topPanel.add(nameLabel);

      buttonGrid = new JButton[size][size];
      ButtonListener actionListener = new ButtonListener(this);
      JPanel middlePanel = new JPanel(new GridLayout(size, size));
      for (int row = 0; row < size; row++) {
         for (int col = 0; col < size; col++) {
            JButton button = new JButton("   ");
            middlePanel.add(button);
            buttonGrid[row][col] = button;
            button.addActionListener(actionListener);
         }
      }

      setLayout(new BorderLayout());
      add(topPanel, BorderLayout.NORTH);
      add(middlePanel, BorderLayout.CENTER);

      // run GUI on Swing event thread
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            JFrame frame = new JFrame("Test");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().add(TicTacToeGame.this);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
         }
      });
   }

   public int getPlayer() {
      return player;
   }

   public synchronized void playOneTurn(final int player)
         throws InterruptedException {
      this.player = player;
      System.out.printf("Player %d before wait%n", player);
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            nameLabel.setText(names[player]);
         }
      });
      synchronized (LOCK) {
         waiting = true;
         while (waiting) {
            LOCK.wait();
         }
      }
   }

   public boolean isFull() {
      for (int row = 0; row < size; row++) {
         for (int col = 0; col < size; col++) {
            if (buttonGrid[row][col].isEnabled()) {
               return false;
            }
         }
      }
      return true;
   }

   public boolean haveWinner() {
      // TODO finish this
      return false;
   }

   public void doNotification() {
      new Thread(new Runnable() {
         public void run() {
            synchronized (LOCK) {
               waiting = false;
               LOCK.notifyAll();
            }
         }
      }).start();
   }

   public void tttButtonPressed(ActionEvent e) {
      AbstractButton source = (AbstractButton) e.getSource();
      for (int r = 0; r < size; r++) {
         for (int c = 0; c < size; c++) {
            if (buttonGrid[r][c] == source) {
               String text = player == 0 ? "X" : "0";
               source.setText(text);
               source.setEnabled(false);
            }
         }
      }
      doNotification();
   }

}

class ButtonListener implements ActionListener {
   private TicTacToeGame ticTacToeGame;

   public ButtonListener(TicTacToeGame ticTacToeGame) {
      this.ticTacToeGame = ticTacToeGame;
   }

   public void actionPerformed(ActionEvent e) {
      ticTacToeGame.tttButtonPressed(e);
   };
}

答案 1 :(得分:2)

哦好的,我发现了这个错误。

public class TicTacToeGame extends JFrame {
    public synchronized void playOneTurn(int player)
            throws InterruptedException {
        wait();
    }

    class ButtonListener implements ActionListener {
        @Override
        public synchronized void actionPerformed(ActionEvent e) {
            notifyAll();
        }
    }
}

我减少了代码,因此更加明显。 playOneTurn已同步,并在TicTacToeGame实例上等待,但actionPerformed已同步,并在ButtonListener实例上发出通知。

修复将是这样的(因为ButtonListener是一个内部类):

@Override
public void actionPerformed(ActionEvent e) {
    synchronized (TicTacToeGame.this) {
        TicTacToeGame.this.notifyAll();
    }
}

或者创建一个单独的对象作为监视器。

但正如我之前所说(并且@HovercraftFullOfEels似乎也在说),您可以简单地从程序中删除while循环,并将该事件用作单个入口点。

另外:不要忘记create your GUI on the EDT with invokeLater

答案 2 :(得分:0)

当ActionListner的代码执行时更耗时,它会暂停程序的所有其他部分的运行。

因此,当控件进入ActionListner时创建一个新线程。因此,当您创建的线程运行代码的ActionListner部分时,它利用主线程运行其他代码。

示例:

    void actionPerformed(ActionEvent e) {

    Thread t = new Thread(new runnable()) {

        public void run() {

            System.out.println("Entering Action Performed");

            for (i = 0; i < size; i++) {

                for (j = 0; j < size; j++) {

                    if (e.getSource() == cells[i][j]) {

                        if (model[i][j] == 'e') {

                            cells[i][j].setText("");

                            currentSpot[0] = i;

                            currentSpot[1] = j;

                            if (count % 2 == 0) {

                                cells[i][j].setBackground(Color.GREEN);

                                cells[i][j].setIcon(X_MARK);

                                model[i][j] = 'x';

                                count++;

                                waiting = false;
                                System.out.println("Boolean hit");

                                notifyAll();

                            } else {

                                cells[i][j].setBackground(Color.CYAN);

                                cells[i][j].setIcon(O_MARK);

                                model[i][j] = 'o';

                                count++;

                                waiting = false;

                                System.out.println("Boolean hit");

                                notifyAll();

                            }

                        } else {
                            System.out
                                    .println("Hey, you can't move there silly!");

                        }
                    }
                }
            }
        }
    };// runnable ends here

}