3 Stimmen

Veranschaulichung der Flüchtigkeit: Ist dieser Code thread-sicher?

Ich versuche, die Verwendung und Bedeutung von volatile mit einem Beispiel, das nicht wirklich zu einem guten Ergebnis führen würde, wenn volatile wurde ausgelassen.

Aber ich bin es nicht wirklich gewohnt, mit volatile . Die Idee des folgenden Codes ist es, eine Endlosschleife auszulösen, wenn volatile weggelassen wird, und vollkommen thread-sicher ist, wenn volatile vorhanden ist. Ist der folgende Code thread-sicher? Haben Sie ein anderes realistisches und kurzes Beispiel für Code, der volatile und würde ohne sie ein offensichtlich falsches Ergebnis liefern?

Hier ist der Code:

public class VolatileTest implements Runnable {

    private int count;
    private volatile boolean stopped;

    @Override
    public void run() {
        while (!stopped) {
            count++;
        }
        System.out.println("Count 1 = " + count);
    }

    public void stopCounting() {
        stopped = true;
    }

    public int getCount() {
        if (!stopped) {
            throw new IllegalStateException("not stopped yet.");
        }
        return count;
    }

    public static void main(String[] args) throws InterruptedException {
        VolatileTest vt = new VolatileTest();
        Thread t = new Thread(vt);
        t.start();
        Thread.sleep(1000L);
        vt.stopCounting();
        System.out.println("Count 2 = " + vt.getCount());
    }
}

10voto

irreputable Punkte 43667

Victor hat recht, es gibt Probleme mit Ihrem Code: Atomarität und Sichtbarkeit.

Hier ist meine Ausgabe:

    private int count;
    private volatile boolean stop;
    private volatile boolean stopped;

    @Override
    public void run() {
        while (!stop) {
            count++; // the work
        }
        stopped = true;
        System.out.println("Count 1 = " + count);
    }

    public void stopCounting() {
        stop = true;
        while(!stopped)
           ; //busy wait; ok in this example
    }

    public int getCount() {
        if (!stopped) {
            throw new IllegalStateException("not stopped yet.");
        }
        return count;
    }

}

Wenn ein Thread feststellt, dass stopped==true ist gewährleistet, dass die Arbeit abgeschlossen wird und das Ergebnis sichtbar ist.

Es gibt eine "happens-before"-Beziehung zwischen flüchtigem Schreiben und flüchtigem Lesen (für dieselbe Variable), d. h. wenn es zwei Threads gibt

   thread 1              thread 2

   action A
       |
 volatile write  
                  \
                     volatile read
                          |  
                       action B

Aktion A geschieht vor Aktion B; Schreibvorgänge in A sind für B sichtbar.

1voto

alf Punkte 8247

Es fiel mir immer schwer, Gleichzeitigkeitsprobleme auf überzeugende Weise zu veranschaulichen: Nun gut, es ist ja alles schön und gut passiert-vor und so weiter, aber warum sollte man sich darum kümmern? Gibt es ein wirkliches Problem? Es gibt viele, viele schlecht geschriebene, schlecht synchronisierte Programme - und sie funktionieren trotzdem meistens.

Früher fand ich einen Urlaubsort in einem " funktioniert die meiste Zeit VS Werke Rhetorik" - aber offen gesagt ist das ein schwacher Ansatz. Was ich also brauchte, war ein Beispiel, das den Unterschied deutlich - und vorzugsweise schmerzhaft - machen würde.

Hier ist also eine Version, die den Unterschied tatsächlich zeigt:

public class VolatileExample implements Runnable {
    public static boolean flag = true; // do not try this at home

    public void run() {
        long i = 0;
        while (flag) {
            if (i++ % 10000000000L == 0)
                System.out.println("Waiting  " + System.currentTimeMillis());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new VolatileExample());
        thread.start();
        Thread.sleep(10000L);
        flag = false;
        long start = System.currentTimeMillis();
        System.out.println("stopping " + start);
        thread.join();
        long end = System.currentTimeMillis();
        System.out.println("stopped  " + end);
        System.out.println("Delay: " + ((end - start) / 1000L));
    }
}

Ein einfacher Lauf zeigt:

Waiting  1319229217263
stopping 1319229227263
Waiting  1319229242728
stopped  1319229242728
Delay: 15

Das heißt, es braucht mehr als zehn Sekunden (15 hier) für einen laufenden Thread zu bemerken, dass es eine jede ändern.

Mit volatile haben Sie:

Waiting  1319229288280
stopping 1319229298281
stopped  1319229298281
Delay: 0

d.h. (fast) sofortiges Verlassen. Die Auflösung von currentTimeMillis liegt bei 10 ms, der Unterschied beträgt also mehr als das 1000-fache.

Beachten Sie, dass es sich um Apples Version von (ex-)Sun JDK handelt, mit -server Option. Die 10-Sekunden-Wartezeit wurde hinzugefügt, damit der JIT-Compiler feststellen kann, ob die Schleife heiß genug ist, und sie optimieren kann.

Ich hoffe, das hilft.

1voto

Gaurav Punkte 211

Weitere Vereinfachung des @Elf-Beispiels, bei dem der andere Thread niemals den Wert erhält, der von einem anderen Thread aktualisiert wurde. Entfernen von System.out.println, da es synchronisierten Code innerhalb von println gibt und out statisch ist, was dem anderen Thread irgendwie hilft, den letzten Wert der Flag-Variable zu erhalten.

public class VolatileExample implements Runnable {
   public static boolean flag = true; 

  public void run() {
     while (flag);
  }

  public static void main(String[] args) throws InterruptedException {
    Thread thread = new Thread(new VolatileExample());
    thread.start();
    Thread.sleep(1000L);
    flag = false;
    thread.join();
  }
}

0voto

Victor Sorokin Punkte 11645

UPDATE Meine Antwort ist falsch, siehe Antwort von irreputable.


Es ist no thread-safe, da Zugang zu count ist nicht es gibt nur einen Schriftsteller-Thread. Sollte es einen weiteren Writer-Thread geben, wird der Wert von count nicht mehr mit der Anzahl der Aktualisierungen übereinstimmen würde.

Sichtbarkeit von count Wert an den Hauptthread wird durch die Überprüfung stopped flüchtig im Inneren getCount Methode. Dies wird als piggybacking on synchronization en Concurrency in practice Buch.

0voto

mre Punkte 42210

Zur Veranschaulichung der Bedeutung der volatile Schlüsselwort, wenn es um Gleichzeitigkeit geht, müssen Sie lediglich sicherstellen, dass die volatile Feld wird in einem separaten Thread geändert und gelesen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X