0

我正在尝试构建一个简单的tictactoe 网络游戏。我需要程序等到玩家移动然后继续。在我的代码底部的 whileConnected() 函数中,我有一个 while(true) 循环,它应该永远运行并在按下按钮时显示一条确认消息(这表明字符串的内容变量“消息”的变化)。

问题是,即使单击按钮时字符串消息变量发生变化,我的 whileConnected() 函数也永远不会意识到这一点,并且函数内的 if 语句永远不会计算为真。ButtonListener 类中的相同 if 语句可以正常工作并显示所需的确认消息。

我怎么解决这个问题?我读了又读,我知道我应该使用线程(我读过它们,但我以前从未使用过它们,这就是为什么它只是一个猜测)。我需要线程吗?有人可以简单地解释一下应该用于这个特定问题的原则吗?(如何让程序暂停直到单击按钮,然后继续使用单击按钮时创建的相关信息)。一个代码示例真的会减轻我对线程的阅读——对于初学者来说,这是一个非常抽象的话题。

以下是我的代码,在此先感谢。

public class Test extends JFrame
{
    private Container contentPane;
    private JButton btn00;
    private static String message = "";

    private class ButtonListener implements ActionListener
    {

        @Override
        public void actionPerformed(ActionEvent e)
        {
            String buttonText = e.getActionCommand();

            if (buttonText.equals("My Button"))
            {
                message = "pressed";
                if (message != "")
                    System.out.println(message+"(executed by ButtonListener)");
            }           
        }

    }

    public Test()
    {
        this.contentPane = this.getContentPane();
        btn00 = new JButton("My Button");
        btn00.setSize(btn00.getPreferredSize());
        btn00.setLocation(20,20);
        ButtonListener listener = new ButtonListener();
        btn00.addActionListener(listener);

        // configure frame
        this.setSize(300,300);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);

        // make panel
        JPanel panel = new JPanel();
        panel.setSize(200,200);
        panel.setLocation(10,10);
        panel.add(btn00);
        this.contentPane.add(panel);        
    }

    public static void main(String[] args)
    {
        Test gui = new Test();
        gui.setVisible(true);
        // connected
        whileConnected();

    }

    private static void whileConnected()
    {
        System.out.println("message is at first empty: "+message);
        while (true)
        {
            // the if below never evaluates to true... why?

            if (message != "") // this is never true
                System.out.println(message+"(executed by whileConnected)");         
        }

    }

}
4

3 回答 3

2

如果您使用的是 swing,那么您已经在使用线程。Swing 本质上具有一个用于 I/O 的线程和一个用于后端的线程。你确实想在这里使用线程——除此之外,让一个线程等待比给它一个无限循环来搅动要便宜得多。

侦听器是线程的另一种应用,如果您可以通过使用结构良好的侦听器获得大部分或全部所需内容,我不会感到惊讶。或者,这些东西称为信号量。信号量是线程处理时间的一种方式——如果一个线程试图锁定一个已经锁定的信号量,它会等到另一个线程解锁它后再继续(并再次锁定它)。在您的情况下,您可以尝试以下方法。

  • 有一个按钮监听器、一个主函数和一个锁定信号量。
  • 主函数启动,执行任何初始行为,并尝试获取信号量。由于信号量已经被锁定,所以它保持不变。
  • 当按钮监听器触发时,它所做的一件事就是解锁信号量。
  • 一旦信号量解锁,主函数就会抓住它(从而再次锁定它)并执行它应该做的任何事情。最终,它完成了,并尝试再次获取(锁定的)信号量,从而等待按钮侦听器下一次触发。
  • 重复。

编辑:包括并解释实际接受的解决方案(来自下面的评论)。

修复:Thread.sleep(1000);在 whileConnected 函数中添加到 while 循环的内部。

说明:while 循环是一个无限循环,只包含一个 if 语句。这个 if 语句在很长一段时间内(至少就计算机而言)评估为假(因此什么也不做)。这与电气短路的作用大致相同——没有什么可以减慢它的速度,因此运行该 main 函数的线程会消耗大量计算资源而无所事事。这是不好的。有可能某些故障安全机制启动并杀死了线程。线程可能以更丑陋的方式失败或破坏了某些东西。在任何情况下,包括一个 sleep 语句(在这种情况下,每个循环都休眠一秒钟,因为它以 ms 为单位)可以防止这种情况发生,从而允许函数无限期地继续。

于 2013-03-27T18:54:39.043 回答
2

Swing 与大多数 GUI 框架一样,是一个事件驱动的环境。基本上这意味着,应用程序将等待某种事件发生,然后将触发任何注册的侦听器,它们正在等待该事件的通知。

在您的情况下,您只需将 a 注册AdtionListener到按钮。当用户单击它(或通过键盘激活)时,actionPerformed将调用该方法,您可以执行所需的任何代码。

查看如何编写动作监听器以获取更多信息。

Swing 也是一个单线程框架。也就是说,与 UI 的所有交互都必须在 Event Dispatching Thread 的上下文中执行。

这也意味着,任何长时间运行或阻塞的任务必须从单独的线程中执行,以免阻塞 EDT 处理传入事件和重绘请求,这可能会使您的应用程序看起来好像它已挂起。

查看Swing 中的并发以获取更多详细信息

在我看来,您的游戏协议将需要某种方式来跟踪它是谁的回合。这将通过使用虚拟令牌简单地实现。

基本概念是,除非玩家拥有令牌,否则他们无法移动。一旦玩家移动,该移动和令牌被发送给其他玩家。

于 2013-03-27T19:22:56.447 回答
0

我认为根据定义,游戏会暂停,直到按下按钮,因为 gui 不会运行主线程,它应该在事件调度线程中运行。

Swing 在线程和侦听器方面肯定有许多陷阱,如果没有正确实施,可能会导致一些不可预知的行为。

您是否阅读过有关 Oracle for Swing 的 Java 教程?最相关的是http://docs.oracle.com/javase/tutorial/uiswing/events/index.html中针对 Listeners 和 http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index的示例.html用于 WorkerThreads

我发现 Swing 最好的办法是在此处下载示例并尝试对其进行扩展

不过,我同意 Ben Barden 的观点,根据我对您需要什么的理解,我认为您可以使用侦听器来实现您所需要的。

于 2013-03-27T19:12:39.447 回答