2012-12-09 14 views
5

Mam dziwne zachowanie pętli while (true). Oto kod:Dziwne zachowanie java z pętlą while i kolejką

Jako członek klasy mam:

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

... i funkcją:

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

Kiedy obiekt zostanie odebrany z serwera (poprzez gniazdo), InputController klasa przekazuje ten obiekt do odpowiedniego kontrolera, w tym przypadku MasterLogInController i umieszcza go w odpowiedzi kolejki. Czekam na odpowiedź w pętli while (true), ale problem polega na tym, że usunę "System.out.printline (" foo ");" pętla zostanie wprowadzona tylko raz !? Z tą linią syso "zmuszam" pętlę do robienia pętli do czasu otrzymania odpowiedzi. Co jest nie tak?

+0

wystarczy umieścić prawdziwe w pętli while, trzeba określić, co ma być prawdą. – DrinkJavaCodeJava

+0

Dla mnie to brzmi jak jakiś rodzaj wyścigu. Wzywasz to w wątku, który zaczynasz, prawda? Spróbuj złapać wszystkie wyjątki w tej metodzie i wydrukuj je. –

+0

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

Odpowiedz

4

Zakładam, że masz uruchomionych kilka wątków.

System.out.println tworzy barierę pamięci, która prawdopodobnie pomoże Twojemu kodowi zobaczyć pewną zmienną, która poza tym nie jest widoczna (z powodu braku synchronizacji).

W szczególności kolejka nie jest bezpieczna dla wątków i wydaje się być bezpiecznie opublikowana. Więc to jest bardzo możliwe, że:

  • pętla while może zobaczyć response jako NULL ==> NullPointerException
  • reponse.isEmpty() może return false ale response.peek() może zwrócić NULL, które następnie oddane do Boolean i unbox w stanie if((Boolean)xxx == true) ==> NullPointerException
  • itp

Oprócz porad dźwięku podany w komentarzach, aby pomóc zrozumieć przyczynę, należy spraw, aby wątek kodu był bezpieczny. Na przykład możesz użyć numeru thread safe BlockingQueue. Ale to prawdopodobnie nie wystarczy (ze względu na sposób, w jaki układane są różne instrukcje if/if/else if oraz fakt, że kolejka może zostać zmieniona przez inny wątek pomiędzy każdą z tych instrukcji).

+0

Dzięki! To wyjaśnia wszystko. – Maleta

+0

Jedyną zmianą, którą musiałem wykonać, było zamiast kolejki Użyłem BlockingQueue response = new LinkedBlockingQueue (); Ale nadal pozostaje dziwne, dlaczego miałem z tym problem, a mój przyjaciel na swoim komputerze nie (mamy tę samą wersję Java - 7u9). – Maleta

+0

Może to zależeć od wielu czynników, w tym parametrów maszyny JVM (-client lub -server), architektury procesora, liczby rdzeni, systemu operacyjnego (na przykład Windowsa i systemu Linux), obciążenia procesora używanego przez inne programy itp. Fakt, że działał on prawidłowo jeden komputer był po prostu zbiegiem okoliczności i nie można na nim polegać. Jeśli uruchomisz program wiele razy na komputerze przyjaciela, może się on w pewnym momencie załamać. – assylias

2

Podejrzewam, że dzieje się tak, że twoja pętla jest zoptymalizowana przez kompilator JIT. Jeśli response.isEmpty() jest prawdziwe, to po raz pierwszy jest wywoływana w pętli i zauważając, że response nie znajduje się w bloku lub metodzie synchronized lub jest oznaczony volatile, prawdopodobnie kompilator JIT zdecyduje, że nie zmieni się i po prostu usunie to, co się pojawi być pustą zajętą ​​pętlą z uruchomionego kodu.

Dodanie w instrukcji println() przynajmniej nadaje pętlę cel, w oczach kompilatora JIT, więc pozostawi to działanie w takim przypadku.

Aby rozwiązać ten problem, oprócz wystający porady udzielonej przez assylias, można umieścić wszystkie odniesienia do response wewnątrz synchronized bloku tak:

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

Gdzie jest twój drugi wątek jest dodanie odpowiedź do kolejki upewnij się, że jest także w zsynchronizowanym bloku i wzywa notifyAll():

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

Również umieścić 'JOptionPane.showMessageDialog()' i 'requestFocus()' wewnątrz wywołania invokeLater, ponieważ powinny one być wywoływane tylko na EDT. –