简体   繁体   English

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

[英]Strange java behavior with while loop and queue

I have strange behavior of while(true) loop. 我有while(true)循环的奇怪行为。 Here is the code: 这是代码:

As a member of class I have: 作为班级成员,我有:

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

... and a function: ......和一个功能:

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;
            }
        }
    }
}

When object is received from server(via Socket), InputController class pass that object to appropriate controller, in this case MasterLogInController and put it in Queue response. 当从服务器接收对象(通过Socket)时,InputController类将该对象传递给适当的控制器,在本例中为MasterLogInController并将其放入Queue响应中。 I am waiting for that response in while(true) loop, but the problem is if I remove "System.out.printline("foo");" 我在while(true)循环中等待响应,但问题是如果我删除“System.out.printline(”foo“);” loop will be entered only once!? 循环只会输入一次!? With this syso line I "force" while loop to do loops until response is received. 使用这个syso行,我“强制”while循环执行循环,直到收到响应。 What's wrong here? 这有什么不对?

I assume you have several threads running. 我假设你有几个线程在运行。

System.out.println creates a memory barrier which probably helps your code see some variable which is otherwise not visible (because of lack of synchronization). System.out.println创建了一个内存屏障,可能会帮助您的代码看到一些不可见的变量(因为缺少同步)。

In particular, your queue is not thread safe and does seem to be safely published. 特别是,您的队列不是线程安全的,似乎安全发布。 So it is very conceivable that: 因此可以想象:

  • your while loop might see response as null ==> NullPointerException 你的while循环可能会看到response为null ==> NullPointerException
  • reponse.isEmpty() might return false but response.peek() might return null, which you then cast to a Boolean and unbox in your condition if((Boolean)xxx == true) ==> NullPointerException reponse.isEmpty()可能返回false,但是response.peek()可能会返回null,然后在你的条件下转换为Boolean和unbox if((Boolean)xxx == true) ==> NullPointerException
  • etc. 等等

Apart from the sound advice given in the comments to help understand the cause, you should make the code thread safe. 除了评论中给出的有助于理解原因的合理建议之外,您还应该使代码线程安全。 For example, you could use a thread safe BlockingQueue . 例如,您可以使用线程安全的BlockingQueue But that will probably not be enough (because of the way your various if / if / else if statements are laid out and the fact that the queue might be changed by another thread between each of those statements). 但这可能还不够(因为你的各种if / if / else if语句的布局方式以及队列可能被每个语句之间的另一个线程更改的事实)。

I suspect what is happening is that your loop is being optimised out of existence by the JIT compiler. 我怀疑正在发生的事情是你的循环被JIT编译器优化了。 If response.isEmpty() is true the first time it is called in your loop, and noticing that response is not inside a synchronized block or method, or marked volatile , it is likely the JIT compiler will decide that it's not going to change and just remove what appears to be an empty busy loop from running code. 如果第一次在循环中调用response.isEmpty()为true,并注意到response不在synchronized块或方法中,或者标记为volatile ,则JIT编译器可能会认为它不会更改并且从运行代码中删除看似空忙的循环。

Adding in the println() statement at least gives the loop a purpose, in the eyes of the JIT compiler, so it will leave it running in that case. 在JIT编译器的眼中,添加println()语句至少给出了循环的目的,因此在这种情况下它将保持运行。

To fix this, in addition to the salient advice given by assylias, you can put all references to response inside a synchronized block like so: 要解决这个问题,除了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();
            }
        });
    }
}

Where your other thread is adding the response to the queue, make sure that is also in an synchronized block and calls notifyAll() : 如果您的其他线程正在将响应添加到队列,请确保它也在同步块中并调用notifyAll()

public void addResult(Object result) {
    synchronized (response) {
        response.add(result);
        response.notifyAll();
    }       
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM