2012-12-09 38 views
5

while(true)loop有奇怪的行为。下面是代码:while循环和队列的奇怪java行为

作为类的成员我有:

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

当从服务器(通过插座)接收到的对象,InputController类将该对象传递给适当的控制器,在这种情况下为MasterLogInController并将其放入Queue响应中。 我正在等待while(true)循环中的响应,但问题是如果我删除“System.out.printline(”foo“);”循环只会输入一次!?有了这个系统行我“强制”while循环做循环,直到收到响应。这里有什么问题?

+0

你只要把真正的while循环,你需要指定什么必须是真实的。 – DrinkJavaCodeJava

+0

对我而言,这听起来像是某种竞争条件。你在开始的线程中调用它,对吧?尝试捕获此方法中的所有例外并打印它们。 –

+0

查看https://stackoverflow.com/questions/25425130/loop-doesnt-see-changed-value-without-a-print-statement – Boann

回答

4

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

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

特别是,您的队列不是线程安全的,似乎是安全发布的。所以这是非常可能的是:

  • while循环可能会看到response为空==> NullPointerException异常
  • reponse.isEmpty()也可能返回false,但response.peek()可能返回null,然后您可以转换为Boolean和拆箱在你的病情if((Boolean)xxx == true) ==> NullPointerException异常

除了在评论中给出了中肯的意见,帮助理解的原因,你应该使代码线程安全。例如,您可以使用thread safe BlockingQueue。但这可能还不够(因为你的各种if/if/else if语句的布局以及这些语句之间的另一个线程可能会改变队列的事实)。

+0

谢谢!这解释了一切。 – Maleta

+0

只有改变我不得不使队列我使用BlockingQueue response = new LinkedBlockingQueue ();但仍然很奇怪,为什么我有这个问题,而我的朋友在他的电脑上没有(我们有相同版本的Java - 7u9)。 – Maleta

+0

它可以取决于许多因素,包括JVM参数(-client或-server),处理器体系结构,内核数量,操作系统(例如Windows与Linux),其他程序使用的CPU负载等等。一台电脑只是巧合,不能依赖。如果您在朋友的电脑上多次运行该程序,它可能会在某个阶段中断。 – assylias

2

我怀疑发生了什么事是你的循环被JIT编译器优化不存在。如果response.isEmpty()第一次在您的循环中调用它,并且注意到response不在​​块或方法内,或者标记为volatile,那么很可能JIT编译器会决定它不会更改,只是删除显示的内容从运行代码变成空闲的忙碌循环。

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

要解决这个问题,除了由assylias给出的突出的建议,你可以把所有引用response一个​​块中,像这样:

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

如果您的其他线程也加入到队列中的响应,确保也是一个synchronized块,并呼吁notifyAll()

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

另外我在调用invokeLater时加入了'JOptionPane.showMessageDialog()'和'requestFocus()',因为它们只应该在EDT上调用。 –