5

我有 while(true) 循环的奇怪行为。这是代码:

作为班上的一员,我有:

static Queue<Object> response = new LinkedList<Object>();

...和一个功能:

private void read() {

    while (true)
    {
        System.out.println("foo");
        if(!(response.isEmpty()))
        {

            if((Boolean)response.peek() == true)
            {
                view.dispose();
                LogInControler controler= new LogInControler();
                disableMasterLogin();
                response.poll();
                return;
            }
            else if((Boolean)response.poll() == false)
            {
                JOptionPane.showMessageDialog(view.getRootPane(), 
                        "Wrong username or password.");
                view.tfUsername.requestFocus();
                return;
            }
        }
    }
}

当从服务器(通过 Socket)接收到对象时,InputController 类将该对象传递给适当的控制器,在本例中为 MasterLogInController 并将其放入队列响应中。我正在等待 while(true) 循环中的响应,但问题是如果我删除 "System.out.printline("foo");" 循环只会输入一次!?使用这条 syso 行,我“强制”while 循环执行循环,直到收到响应。这里有什么问题?

4

2 回答 2

4

我假设你有几个线程正在运行。

System.out.println创建一个内存屏障,它可能会帮助您的代码看到一些不可见的变量(因为缺乏同步)。

特别是,您的队列不是线程安全的,并且似乎已安全发布。所以很有可能:

  • 您的while循环可能会response显示为 null ==> NullPointerException
  • reponse.isEmpty()可能会返回 false 但response.peek()可能会返回 null,然后您将其转换为 aBoolean并在您的条件中取消框if((Boolean)xxx == true)==> NullPointerException
  • 等等

除了注释中给出的有助于理解原因的合理建议外,您还应该使代码线程安全。例如,您可以使用线程安全的 BlockingQueue。但这可能还不够(因为您的各种 if / if / else if 语句的布局方式以及队列可能在每个语句之间被另一个线程更改的事实)。

于 2012-12-09T23:26:59.610 回答
2

我怀疑正在发生的事情是 JIT 编译器正在优化您的循环而不存在。如果response.isEmpty()第一次在循环中调用它时为真,并注意到它response不在synchronized块或方法内,或者标记volatile为运行代码的繁忙循环。

在 JIT 编译器的眼中,添加println()语句至少给循环一个目的,所以它会让它在这种情况下运行。

为了解决这个问题,除了 assylias 给出的重要建议外,您还可以将所有引用response放在一个synchronized块中,如下所示:

public void read() {
    Boolean result = null;
    synchronized (response) {
        while (true) {
            result = (Boolean) response.poll();
            if (result != null) break;
            try {
                response.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
                // You could put return; here
            }
        }
    }
    // result should always be non null here
    if (result) {
         view.dispose();
         LogInControler controler = new LogInControler();
         disableMasterLogin();
    } else {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JOptionPane.showMessageDialog(view.getRootPane(), "Wrong username or password");
                view.tfUsername.requestFocus();
            }
        });
    }
}

如果您的其他线程将响应添加到队列中,请确保它也在同步块中并调用notifyAll()

public void addResult(Object result) {
    synchronized (response) {
        response.add(result);
        response.notifyAll();
    }       
}
于 2012-12-10T00:26:32.220 回答