Как правильно реализовать оператор wait();?

#java #swing #illegalmonitorstateexcep

#java #swing #illegalmonitorstateexcep

Вопрос:

У меня возникает проблема, когда я пытаюсь создать игру в крестики-нолики. Проблема, с которой я сталкиваюсь, возникает, когда я пытаюсь использовать оператор wait();, чтобы приостановить игру, чтобы игрок мог посмотреть, кто выиграл игру и каковы выигрышные квадраты, однако, когда я пытаюсь запустить этот небольшой фрагмент кода прямо здесь:

             wait(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
 

Он выдает мне этот код ошибки прямо здесь:

 Exception in thread "AWT-EventQueue-0" java.lang.IllegalMonitorStateException
    at java.lang.Object.wait(Native Method)
    at GameCode.restart(GameCode.java:264)
    at GameCode.xWins(GameCode.java:245)
    at GameCode.check(GameCode.java:171)
    at GameCode.actionPerformed(GameCode.java:70)
    at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
    at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
    at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
    at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
    at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
    at java.awt.Component.processMouseEvent(Unknown Source)
    at javax.swing.JComponent.processMouseEvent(Unknown Source)
    at java.awt.Component.processEvent(Unknown Source)
    at java.awt.Container.processEvent(Unknown Source)
    at java.awt.Component.dispatchEventImpl(Unknown Source)
    at java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
    at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
    at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
    at java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.awt.Window.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
    at java.awt.EventQueue.access$500(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue$4.run(Unknown Source)
    at java.awt.EventQueue$4.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)
 

Он описывает его как an IllegalMonitorStateException , однако я пытался использовать throw для решения этой проблемы, но он по-прежнему продолжает выдавать мне эту ошибку. Какие-либо решения существуют? Для справки вот мой код, если вам нужно увидеть какую-либо другую информацию:

 import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class GameCode implements ActionListener {
    
    Random random = new Random();
    JFrame frame = new JFrame();
    JPanel title_panel = new JPanel();
    JPanel button_panel = new JPanel();
    JLabel textfield = new JLabel();
    JButton[] buttons = new JButton[9];
    boolean player1_turn;
    
    
    
    
    GameCode() {
        
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(500,500);
        frame.getContentPane().setBackground(new Color(0,0,0));
        frame.setLayout(new BorderLayout());
        frame.setVisible(true);
        
        textfield.setBackground(new Color(25,25,25));
        textfield.setForeground(new Color(25,255,0));
        textfield.setFont(new Font("TimesRoman", Font.BOLD,75));
        textfield.setHorizontalAlignment(JLabel.CENTER);
        textfield.setText("Tic Tac Toe");
        textfield.setOpaque(true);
        title_panel.setLayout(new BorderLayout());
        title_panel.setBounds(0,0,800,100);
        
        button_panel.setLayout(new GridLayout(3,3));
        button_panel.setBackground(new Color(150,25,25));
        
        for(int j = 0; j < 9; j   ) {
            
            buttons[j] = new JButton();
            button_panel.add(buttons[j]);
            buttons[j].setFont(new Font("Ink Free", Font.BOLD,120));
            buttons[j].setFocusable(false);
            buttons[j].addActionListener(this);
            
        }
        
        
        title_panel.add(textfield);
        frame.add(title_panel, BorderLayout.NORTH);
        frame.add(button_panel);
        
        Turnone();
        
    }

    @Override
    public void actionPerformed(ActionEvent arg0) {
        
        for (int i = 0; i < 9; i  ) {
            
            if(arg0.getSource()==buttons[i]) {
                if(player1_turn) {
                    if(buttons[i].getText()=="") {
                        buttons[i].setForeground(new Color(255,0,0));
                        buttons[i].setText("X");
                        player1_turn=false;
                        textfield.setText("O turn");
                        check();
                    }
                } else {
                    if(buttons[i].getText()=="") {
                        buttons[i].setForeground(new Color(0,0,255));
                        buttons[i].setText("O");
                        player1_turn=true;
                        textfield.setText("X turn");
                        check();
                    }
                }
            }
        }
    }//Creating the Buttons the player will interact with
    
    public void Turnone() {
        
        for(int i=0; i<9;i  ) {
            buttons[i].setEnabled(false);
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        for(int i=0; i<9;i  ) {
            buttons[i].setEnabled(true);
        }
        
        if(random.nextInt(2)==0) {
            
            player1_turn = true;
            textfield.setText("X turn");
            
        } else {
            
            player1_turn = false;
            textfield.setText("O Turn");
            
        }
        
    }//Using a random int to decide which player will go first
    
    public void check() {
        //X Wins
        
        if(
                (buttons[0].getText()=="X") amp;amp;
                (buttons[1].getText()=="X") amp;amp;
                (buttons[2].getText()=="X") 
                ) {
            xWins(0,1,2);
        }
        if(
                (buttons[3].getText()=="X") amp;amp;
                (buttons[4].getText()=="X") amp;amp;
                (buttons[5].getText()=="X") 
                ) {
            xWins(3,4,5);
        }
        if(
                (buttons[6].getText()=="X") amp;amp;
                (buttons[7].getText()=="X") amp;amp;
                (buttons[8].getText()=="X") 
                ) {
            xWins(6,7,8);
        }
        if(
                (buttons[0].getText()=="X") amp;amp;
                (buttons[3].getText()=="X") amp;amp;
                (buttons[6].getText()=="X") 
                ) {
            xWins(0,3,6);
        }
        if(
                (buttons[1].getText()=="X") amp;amp;
                (buttons[4].getText()=="X") amp;amp;
                (buttons[7].getText()=="X") 
                ) {
            xWins(1,4,7);
        }
        if(
                (buttons[2].getText()=="X") amp;amp;
                (buttons[5].getText()=="X") amp;amp;
                (buttons[8].getText()=="X") 
                ) {
            xWins(2,5,8);
        }
        if(
                (buttons[0].getText()=="X") amp;amp;
                (buttons[4].getText()=="X") amp;amp;
                (buttons[8].getText()=="X") 
                ) {
            xWins(0,4,8);
        }
        if(
                (buttons[2].getText()=="X") amp;amp;
                (buttons[4].getText()=="X") amp;amp;
                (buttons[6].getText()=="X") 
                ) {
            xWins(2,4,6);
        }
        //O Wins
        
        if(
                (buttons[0].getText()=="O") amp;amp;
                (buttons[1].getText()=="O") amp;amp;
                (buttons[2].getText()=="O") 
                ) {
            oWins(0,1,2);
        }
        if(
                (buttons[3].getText()=="O") amp;amp;
                (buttons[4].getText()=="O") amp;amp;
                (buttons[5].getText()=="O") 
                ) {
            oWins(3,4,5);
        }
        if(
                (buttons[6].getText()=="O") amp;amp;
                (buttons[7].getText()=="O") amp;amp;
                (buttons[8].getText()=="O") 
                ) {
            oWins(6,7,8);
        }
        if(
                (buttons[0].getText()=="O") amp;amp;
                (buttons[3].getText()=="O") amp;amp;
                (buttons[6].getText()=="O") 
                ) {
            oWins(0,3,6);
        }
        if(
                (buttons[1].getText()=="O") amp;amp;
                (buttons[4].getText()=="O") amp;amp;
                (buttons[7].getText()=="O") 
                ) {
            oWins(1,4,7);
        }
        if(
                (buttons[2].getText()=="O") amp;amp;
                (buttons[5].getText()=="O") amp;amp;
                (buttons[8].getText()=="O") 
                ) {
            oWins(2,5,8);
        }
        if(
                (buttons[0].getText()=="O") amp;amp;
                (buttons[4].getText()=="O") amp;amp;
                (buttons[8].getText()=="O") 
                ) {
            oWins(0,4,8);
        }
        if(
                (buttons[2].getText()=="O") amp;amp;
                (buttons[4].getText()=="O") amp;amp;
                (buttons[6].getText()=="O") 
                ) {
            oWins(2,4,6);
        }
    
    }//Stating all the win conditions for the game
    
    public void xWins(int a, int b, int c) {
        buttons[a].setBackground(Color.GREEN);
        buttons[b].setBackground(Color.GREEN);
        buttons[c].setBackground(Color.GREEN);
        
        for(int i=0; i<9;i  ) {
            buttons[i].setEnabled(false);
        }
        
        textfield.setText("X Wins!");
        
        restart();
    }//When Player X wins
    
    public void oWins(int a, int b, int c) {
        buttons[a].setBackground(Color.GREEN);
        buttons[b].setBackground(Color.GREEN);
        buttons[c].setBackground(Color.GREEN);
        
        for(int i=0; i<9;i  ) {
            buttons[i].setEnabled(false);
        }
        textfield.setText("O Wins!");
        
        restart();
    }//When Player O wins 
    
    public void restart() { 
        
        try {
            wait(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        
    }
    
    
    
}//GameCode
 

Комментарии:

1. Не используйте wait для этого, это не то, что вы хотите.

2. Да, я также пытался использовать Thread.sleep() , однако это остановило всю программу, а не один метод.

3. Да, спать в потоке отправки событий тоже не очень хорошая идея.

4. Есть ли у вас какие-либо предложения относительно того, что я должен использовать?

5. Поместите код, который вы хотите выполнить после ожидания, в его собственный метод. Затем вы можете использовать таймер для его вызова. Вы также можете просто создать лямбда-выражение.

Ответ №1:

Вот простой пример использования таймера swing.

 public void restart() { 
    Timer t = new Timer( 1000, evt->{
        //reset and enable the board.
        for(int i=0; i<9;i  ) {
            buttons[i].setText("_");
            buttons[i].setEnabled(true);
        }
    }
    t.setRepeats(false);
    t.start();
}
 

Это каждый раз создает новый таймер и не отслеживает его. Таким образом, есть определенные улучшения в управлении кодом. Мы надеемся, что это иллюстрирует способ планирования событий в swing.

Ошибка, которую вы получаете с кодом ожидания, заключается в том, что вы должны быть синхронизированы с объектом, чтобы вызвать wait для него. Вы могли бы заменить свой вызов wait на wait synchronize, чтобы устранить ошибку, но это не очень хорошая практика и сделает ваш графический интерфейс невосприимчивым, пока EDT ожидает.

 synchronize(this){
    wait(1000); 
}
 

Комментарии:

1. Я ценю помощь здесь! Я не смог найти метод, который работает для меня, поэтому я ценю возможное решение!

2. Ожидание является частью системы уведомлений о потоках, оно не предназначено для использования для приостановки на определенное время.

3. @assylias я не уверен, почему вы упоминаете об этом здесь. Единственная причина, по которой я добавил часть ожидания, заключалась в том, чтобы показать, что вызывает фактическую ошибку, которую получает op. Возможно, ваш комментарий будет уместен для вопроса.

4. @JohnMcIntyre Я изменил этот ответ, чтобы показать, что вы могли бы сделать. Это лучше, чем запускать новый поток, потому что он выполняет работу над edt и, по сути, сохраняет ваше приложение однопоточным. После того, как кто-то выигрывает, ваш код отключает все кнопки, затем вызывает этот метод, который ждет некоторое время и повторно включает их с помощью прослушивателя действий в edt.

5. @matt Когда вы говорите, что это не очень хорошая практика и заставит EDT ждать, я думал, вы имели в виду, что это wait работает так sleep , когда wait будет «останавливаться» только до отправки сигнала уведомления. Но в любом случае я согласен, что это не очень хорошая идея.

Ответ №2:

Вы слишком много делаете в потоке событий, включая проверку выигрышей и ожидание. Таким образом, следующие изменения будут работать в крайнем случае.

Сначала поместите все тело actionPerformed метода в отдельный поток.

     @Override
    public void actionPerformed(ActionEvent arg0) {
        new Thread(()-> {
        for (int i = 0; i < 9; i  ) {
            
            if(arg0.getSource()==buttons[i]) {
                if(player1_turn) {
                    if(buttons[i].getText().equals("")) {
                        buttons[i].setForeground(new Color(255,0,0));
                        buttons[i].setText("X");
                        player1_turn=false;
                        textfield.setText("O turn");
                        check();
                    }
                } else {
                    if(buttons[i].getText().equals("")) {
                        buttons[i].setForeground(new Color(0,0,255));
                        buttons[i].setText("O");
                        player1_turn=true;
                        textfield.setText("X turn");
                        check();
                    }
                }
            }
        }
        }).start();
    }
 

Тогда будет безопасно использовать sleep в вашем методе перезапуска. Вы также можете использовать таймер переключения, но по-прежнему важно не выполнять слишком много обработки в потоке событий.

 public void restart() {
   try {
       Thread.sleep(2000);
   } catch (InterruptedException ie) {
   }
   System.out.println("Waking up");
  
}

 

Но ваш общий подход имеет некоторые проблемы, когда дело доходит до рисования и обработки событий. Вам следует ознакомиться с учебными пособиями по Java, чтобы узнать больше о рисовании и обработке событий.

Комментарии:

1. Я ценю вашу помощь! Я попробую и добавлю еще один комментарий, если у меня возникнут какие-либо проблемы (если вы не возражаете ответить на них).

2. Это было бы хорошо. Но они должны быть отдельными вопросами в соответствии с политикой SO.

3. Хорошо, я сделаю это, если у меня возникнут какие-либо проблемы.

4. Как вы думаете, в чем компромисс: проверка условий выигрыша в крестики-нолики в EDT против обновления компонентов swing из EDT?

5. @matt Я не знаю. Я просто стараюсь делать как можно меньше в EDT. Обычно это означает заблаговременную подготовку или просто запуск потока в EDT, чтобы избежать блокировки / задержки. Проверка условий в EDT, вероятно, чище, если это не занимает слишком много времени и нет проблем с эффективным конечным требованием в лямбда.