2015-05-27 11 views
5

Esecuzione di questo codice, mi aspetto che aumenti la variabile di test per 5 secondi e quindi termini.Tempo durante il ciclo non terminato

import java.util.Timer; 
import java.util.TimerTask; 

public class Test { 
    private static boolean running; 

    public static void main(String[] args) { 
     long time = 5 * 1000;  // converts time to milliseconds 
     long test = Long.MIN_VALUE; 
     running = true; 

     // Uses an anonymous class to set the running variable to false 
     Timer timer = new Timer(); 
     timer.schedule(new TimerTask() { 
      @Override 
      public void run() { running = false; } 
     }, time); 

     while(running) { 
      test++; 
     } 

     timer.cancel(); 
     System.out.println(test); 
    } 
} 

Tuttavia quando l'eseguo il programma non termina (presumo, mi hanno dato un ragionevole lasso di tempo). Tuttavia se cambio il ciclo while per

while(running) { 
     System.out.println(); 
     test++; 
    } 

Il programma termina nella quantità prevista di tempo (e stampa un sacco di linee). Non capisco. Perché questo comportamento si verifica?

+0

Cosa succede se esegui "volatile"? –

+0

Ho appena testato il codice utilizzando JDK 7 con IntelliJ 11 ed entrambe le versioni sono state completate in pochi secondi. Cosa stai usando per correre? –

+0

@AndyTurner wow, non ho mai incontrato prima la parola chiave volatile. Questo fa funzionare il codice. Grazie! – DenverCoder9

risposta

4

In base a Java Memory Model non è possibile garantire che il campo non volatile sia visibile da un altro thread. Nel tuo caso il tuo metodo principale è compilato JIT e JIT-compiler suppone ragionevolmente che come corrente thread non modifica la variabile running in un ciclo, quindi non dovremmo preoccuparci di leggerlo su ogni iterazione e convertire il ciclo in quello infinito. C'è anche FindBugs warning riguardo a questo caso.

Quando si aggiunge una chiamata System.out.println, sembra che il compilatore JIT non sia in linea, quindi non si può essere certi che questo metodo non modifichi la variabile running, pertanto disattiva l'ottimizzazione della lettura del campo. Tuttavia ciò non dovrebbe essere considerato una soluzione al problema: è possibile che le versioni più recenti di Java siano più intelligenti e ottimizzino il ciclo anche se si dispone di System.out.println all'interno.

+0

Ho accettato questa risposta perché collega informazioni aggiuntive. Grazie! – DenverCoder9

2

Lets avvicinarsi nel codice ...

Se il debug del codice, funzionerà. Questo perché vedrai solo un thread e nessuna cache. Java può utilizzare meccanismi di cache basati su thread. Devi leggerlo e scriverlo nella memoria principale.

Quindi, se si utilizza la parola chiave volatile sulla variabile in esecuzione, jvm lo vedrà come modificabile da più thread e non dovrebbe essere memorizzato nella cache.

Pertanto nella propria situazione la soluzione è l'aggiunta di volatile alla variabile in esecuzione.

2

Si sta aggiornando la variabile running in un thread diverso dal metodo main. Il modello di memoria Java è tale da non garantire la visualizzazione degli aggiornamenti delle variabili non volatili in thread diversi.

La soluzione più semplice consiste nel rendere la variabile volatile: forza il valore "corrente" della variabile da controllare quando il thread lo accede.

Altre soluzioni includono l'utilizzo AtomicBoolean anziché boolean, e avvolgendo accesso a running in reciprocamente synchronized blocchi (cioè il codice che accede running è sincronizzato sullo stesso monitor come il codice che aggiorna).

Si consiglia vivamente di prendere una copia di Java Concurrency In Practice, che descrive questo problema in dettaglio.

Problemi correlati