2015-05-27 17 views
5

Wenn ich diesen Code ausführe, würde ich erwarten, dass er die Testvariable für 5 Sekunden erhöht und dann beendet.Zeitgesteuerte While-Schleife, die nicht beendet wird

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

Jedoch, wenn ich es das Programm läuft nicht am Ende (ich nehme an, ich habe es eine angemessene Menge an Zeit gegeben). Allerdings, wenn ich die while-Schleife zu

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

Das Programm endet in der erwarteten Höhe der Zeit (und druckt viele Linien) ändern. Ich verstehe nicht. Warum tritt dieses Verhalten auf?

+0

Was passiert, wenn Sie "volatile" ausführen? –

+0

Ich habe gerade Ihren Code mit JDK 7 mit IntelliJ 11 getestet und beide Versionen sind in wenigen Sekunden fertig. Was benutzt du um zu laufen? –

+0

@AndyTurner wow, ich bin noch nie auf das flüchtige Keyword gestoßen. Das macht den Code funktioniert. Vielen Dank! – DenverCoder9

Antwort

4

Gemäß Java Memory Model gibt es keine Garantie, wenn ein nichtflüchtiges Feld von einem anderen Thread sichtbar ist. In Ihrem Fall ist Ihre Hauptmethode JIT kompiliert und JIT-Compiler vernünftigerweise davon ausgegangen, dass aktuelle Thread die running Variable in einer Schleife nicht ändert, dann sollten wir nicht bei jeder Iteration lesen und konvertieren Sie die Schleife in die unendliche. Es gibt sogar FindBugs warning in diesem Fall.

Wenn Sie einen System.out.println Aufruf hinzufügen, scheint, dass JIT-Compiler kann nicht inline es, so dass es nicht sicher sein kann, dass diese Methode die running Variable nicht ändert, damit es die Feldleseoptimierung deaktiviert. Dies sollte jedoch nicht als Problemlösung betrachtet werden: Es ist möglich, dass neuere Versionen von Java intelligenter sind und Ihre Schleife optimieren, selbst wenn Sie System.out.println haben.

+0

Ich habe diese Antwort akzeptiert, da sie zusätzliche Informationen verknüpft. Vielen Dank! – DenverCoder9

2

Lets näher in den Code bekommen ...

Wenn Sie Ihren Code debuggen, wird es funktionieren. Das liegt daran, dass nur ein Thread und kein Cache angezeigt wird. Java kann Thread-basierte Cache-Mechanismen verwenden. Sie müssen es lesen und in den Hauptspeicher schreiben.

Wenn Sie also das Schlüsselwort volatile auf Ihrer laufenden Variablen verwenden, wird es von jvm als durch mehrere Threads editierbar angesehen, und es sollte nicht zwischengespeichert werden.

Daher fügt in Ihrer Situation Lösung volatile zu Ihrer laufenden Variable hinzu.

2

Sie aktualisieren die running Variable in einem anderen Thread als Ihre main Methode. Das Java-Speichermodell ist so gestaltet, dass Aktualisierungen nichtflüchtiger Variablen in verschiedenen Threads nicht garantiert sind.

Die einfachste Lösung besteht darin, die Variable volatile: Dies zwingt den 'aktuellen' Wert der Variablen zu überprüfen, wenn der Thread darauf zugreift.

Andere Lösungen umfassen AtomicBoolean anstelle von boolean verwenden und in zueinander synchronized blockiert den Zugriff auf running Umhüllung (d.h. der Code, der running Zugriffe auf demselben Monitor wie der Code synchronisiert, die er aktualisiert).

Ich empfehle dringend, dass Sie eine Kopie von Java Concurrency In Practice abholen, die dieses Problem im Detail beschreibt.

Verwandte Themen