while循环和队列的奇怪java行为

时间:2012-12-09 23:08:59

标签: java while-loop queue

我有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并将其放入Queue响应中。 我在while(true)循环中等待响应,但问题是如果我删除“System.out.printline(”foo“);”循环只会输入一次!?使用这个syso行,我“强制”while循环执行循环,直到收到响应。这有什么不对?

2 个答案:

答案 0 :(得分:4)

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

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

特别是,您的队列不是线程安全的,似乎安全发布。因此可以想象:

  • 您的while循环可能会将response视为null ==&gt;的NullPointerException
  • reponse.isEmpty()可能会返回false,但response.peek()可能会返回null,然后您会在Boolean ==&gt;条件下转换为if((Boolean)xxx == true)和unbox的NullPointerException

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

答案 1 :(得分:2)

我怀疑正在发生的事情是你的循环被JIT编译器优化了。如果第一次在循环中调用response.isEmpty()为真,并注意到response不在synchronized块或方法中,或标记为volatile,则很可能JIT编译器将决定它不会改变,只是从运行代码中删除看似空忙的循环。

println()语句中添加至少为JIT编译器提供循环的目的,因此在这种情况下它将保持运行。

要解决此问题,除了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();
    }       
}