2016-07-17 23 views
1

sich das folgende Szenario aus Memory Consistency - happens-before relationship in Java entlehnt:Konnte Java thread.start neu geordnet werden?

package happen.before; 

public class HappenBeforeRelationship { 

private static int counter = 0; 

private static void threadPrintMessage(String msg){ 
    System.out.printf("[Thread %s] %s\n", Thread.currentThread().getName(), msg); 
} 

public static void main(String[] args) { 
    threadPrintMessage("Increase counter: " + ++counter); 
    Thread t = new Thread(new CounterRunnable()); 
    t.start(); 
    try { 
     t.join(); 
    } catch (InterruptedException e) { 
     threadPrintMessage("Counter is interrupted"); 
    } 
    threadPrintMessage("Finish count: " + counter); 
} 

private static class CounterRunnable implements Runnable { 
    @Override 
    public void run() { 
     threadPrintMessage("start count: " + counter); 
     counter++; 
     threadPrintMessage("stop count: " + counter); 
    } 
} 

Ich weiß, es gibt eine Regel in JLS garantiert, dass Thread.start vor allen Aktionen im gestarteten Thread passiert.

Wenn eine Anweisung ruft Thread.start, jede Aussage, die auch mit dieser Aussage eine passiert-vor-Beziehung hat eine passiert-vor-Beziehung mit jedem von dem neuen Thread ausgeführten Anweisung. Die Auswirkungen des Codes, der zur Erstellung des neuen -Threads führte, sind für den neuen Thread sichtbar.

Aber es behauptet nicht, dass Aussagen vor Thread.start eine passiert-vor-Beziehung damit hat.

Also frage ich mich, dass könnte Thread.start neu geordnet werden, so dass das Programm nicht die erwartete Ausgabe erhalten konnte (Zähler = 2)? Wenn nicht, welcher Teil von JLS gibt an, dass Thread.start nicht nachbestellt werden konnte?


Eine weitere Frage:

Was passiert, wenn join() nach threadPrintMessage("Finish count: " + counter); gesetzt wird? Ist es möglich, dass stop count: 1 gedruckt wird?

+1

Unabhängig von der Neuordnung des Starts ist die Ausgabe nicht unbedingt 2, da die Sichtbarkeit von Aktualisierungen des Zählers nicht garantiert ist. –

+0

@AndyTurner, könnten Sie die zweite Antwort in http://stackoverflow.com/questions/16248898/memory-consistency-happens-before-relationship-in-java überprüfen? Es weist darauf hin, dass 'JLS garantiert, dass das Aufrufen von t.start() die Änderung von x in t.run() sichtbar macht, so dass y garantiert 1 'zugewiesen wird, also ist es eine falsche Aussage? –

+0

@ Chainro das ist definitiv richtig. Von oben nach unten, wie gesagt. Vor dem 'Thread.start' sind Sie single-threaded, so dass die natürliche Reihenfolge respektiert wird. – Dici

Antwort

0

Es gibt eine Ordnungsbeziehung zwischen den Aktionen vor dem Thread.start Aufruf und der Start des neuen Thread aufgrund JLS§17.4.5:

  • Wenn x und y sind Aktionen des gleichen Themas und x kommt vor y in Programmreihenfolge, dann hb (x, y).
  • ...
  • Wenn hb (x, y) undhb (y, z), dann hb (x, z).

und später im selben Abschnitt, der bereits in Ihrer Frage Garantie erwähnt:

  • Ein Aufruf start() an einem Faden passiert-vor alle Aktionen im gestarteten Thread .

Aufgrund der Transitivität von passiert-vor Beziehungen, Sie haben ein passiert-vor Beziehung zwischen der Wirkung des Haupt-Thread vor start() und den Aktionen innerhalb des gestarteten Thread aufgerufen wird.In ähnlicher Weise haben Sie eine passiert vor Beziehung zwischen den Aktionen des gestarteten Thread und erfolgreich Rückkehr des Hauptthreads join Aufruf.

Mit anderen Worten, solange Sie keine InterruptedException erleben, sind die Updates ordnungsgemäß bestellt und das Druckergebnis wird 2 sein. Dies bedeutet jedoch nicht, dass die Aktionen werden nie neu geordnet wie die JLS§17.4.5 weiter:

Es sollte beachtet werden, dass die Anwesenheit eines passiert-vor Beziehung zwischen zwei Aktionen nicht zwangsläufig bedeuten, dass Sie müssen in dieser Reihenfolge in einer Implementierung stattfinden. Wenn die Neuordnung zu Ergebnissen führt, die mit einer legalen Ausführung übereinstimmen, ist dies nicht illegal.

Mit anderen Worten, passiert-vor Beziehungen ein High-Level-Konzept sind, dass Sie rechtliche Ergebnisse einer Programmausführung bestimmen können. Wenn, wie in diesem Beispiel, in der counter am Ende des Programms das einzige rechtliche Ergebnis ist (wie gesagt, unter der Annahme, dass keine Unterbrechung aufgetreten ist), könnte die JVM den Code so viel wie er wünscht arrangieren, solange die Programm wird das rechtliche Ergebnis von 2 in der counter am Ende produzieren.

Sie sollten aufhören, darüber nachzudenken, was nachbestellt werden könnte oder nicht. Das ist ein unwichtiges Implementierungsdetail. In der Abwesenheit der richtigen passiert-vor Beziehungen können Sie Updates auftreten, die out-of-order auftreten, aber auch Updates verpassen oder inkonsistente Zustände erstellen, so macht es keinen Sinn, sich auf ein Detail der Implementierung konzentrieren, die zu unbeabsichtigten Seite führen könnte Effekte in defekten Programmen. Konzentrieren Sie sich stattdessen darauf, was ein richtiges Programm ausmacht.

+0

Warum denken Sie, dass '++ counter' in der Programmreihenfolge vor' thread.start' steht? Mit anderen Worten, ich glaube nicht, dass "++ counter" vor "thread.start" passiert. –

+1

Was denken Sie, ist die "Programmreihenfolge"? Das ist die Reihenfolge Ihrer Aussagen, wie Sie es geschrieben haben. Es ist die gleiche Reihenfolge, die Anweisungen würden ausgeführt, wenn keine anderen Threads vorhanden wären. Sogar Ihre Frage bestätigt implizit, dass '++ counter' vor' thread.start' steht, da ich das nie gesagt habe. Sie haben an diese Reihenfolge gedacht, als ich über die Programmreihenfolge sprach, ohne bestimmte Aktionen zu nennen. – Holger

+0

Im Programm wird '++ counter' vor' thread.start' gesetzt, aber die Programmreihenfolge garantiert nur die Reihenfolge der Variablen, die Abhängigkeiten https://docs.oracle haben.com/javase/specs/jls/se8/html/jls-17.html # jls-17.4.3. –