2012-12-09 14 views
5

Ho uno strano comportamento durante il ciclo (vero). Ecco il codice:Strano comportamento java con ciclo while e coda

qualità di membro della classe che ho:

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

... e una funzione:

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

Quando un oggetto viene ricevuto dal server (via Socket), InputController classificare tale oggetto sul controller appropriato, in questo caso MasterLogInController e inserirlo in risposta in coda. Sto aspettando quella risposta mentre ciclo (vero), ma il problema è se rimuovo "System.out.printline (" foo ");" il ciclo verrà inserito solo una volta !? Con questa linea di syso "forza" mentre il ciclo esegue i loop fino a quando la risposta non viene ricevuta. Cosa c'è che non va qui?

+0

basta mettere vero nel ciclo while, è necessario specificare che cosa deve essere vero. – DrinkJavaCodeJava

+0

Per me, questo suona come una sorta di condizione di gara. Si invoca questo in una discussione che si avvia, giusto? Prova a catturare tutte le eccezioni in questo metodo e stampale. –

+0

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

risposta

4

Suppongo che tu abbia più thread in esecuzione.

System.out.println crea una barriera di memoria che probabilmente aiuta il codice a vedere alcune variabili che altrimenti non sono visibili (a causa della mancanza di sincronizzazione).

In particolare, la coda non è protetta da thread e sembra essere pubblicata in modo sicuro. Quindi è molto plausibile che:

  • il ciclo while potrebbe vedere response come null ==> NullPointerException
  • reponse.isEmpty() potrebbe restituire false ma response.peek() potrebbe restituire null, che poi si lanci in una Boolean e Unbox nella tua condizione if((Boolean)xxx == true) ==> NullPointerException
  • ecc

a parte il buon consiglio dato nei commenti per aiutare a capire la causa, si dovrebbe rendere sicuro il thread del codice. Ad esempio, è possibile utilizzare uno thread safe BlockingQueue. Ma questo probabilmente non sarà abbastanza (a causa del modo in cui le varie dichiarazioni if ​​/ if/else sono disposte e il fatto che la coda potrebbe essere cambiata da un altro thread tra ognuna di quelle dichiarazioni).

+0

Grazie! Questo spiega tutto. – Maleta

+0

Solo la modifica che ho dovuto effettuare è invece di Coda Ho usato BlockingQueue response = new LinkedBlockingQueue (); Ma rimane ancora strano il motivo per cui ho avuto problemi con questo, e il mio amico sul suo computer no (abbiamo la stessa versione di Java - 7u9). – Maleta

+0

Può dipendere da molti fattori, inclusi i parametri JVM (-client o -server), architettura del processore, numero di core, sistema operativo (Windows vs. Linux per esempio), carico della CPU utilizzato da altri programmi ecc. Il fatto che abbia funzionato un computer era solo una coincidenza e non si può fare affidamento. Se esegui il programma molte volte sul computer del tuo amico, potrebbe rompersi a un certo punto. – assylias

2

Ho il sospetto che ciò che sta accadendo è che il tuo ciclo è stato ottimizzato dal compilatore JIT. Se è vero la prima volta che viene richiamato nel ciclo, e notando che response non si trova all'interno di un blocco o metodo synchronized o contrassegnato con volatile, è probabile che il compilatore JIT decida che non cambierà e rimuoverà solo ciò che appare essere un ciclo occupato vuoto dal codice in esecuzione.

L'aggiunta dell'istruzione println() fornisce almeno al ciclo uno scopo, agli occhi del compilatore JIT, quindi in questo caso lo lascerà in esecuzione.

Per risolvere questo problema, oltre ai consigli saliente data dal assylias, è possibile inserire tutti i riferimenti a response all'interno di un blocco synchronized in questo modo:

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

Dove il tuo altro thread sta aggiungendo la risposta alla coda , verificare che sia anche in un blocco sincronizzato e chiede notifyAll():

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

Inoltre ho inserito 'JOptionPane.showMessageDialog()' e 'requestFocus()' all'interno di una chiamata su invokeLater poiché dovrebbero essere richiamati solo sull'EDT. –

Problemi correlati