2016-04-09 7 views
0

Schauen Sie sich die Ausgabe an, obwohl die Methode synchronisiert wurde, ist die Ausgabe nicht geordnet. Da beide Threads dasselbe Objekt "Job" auf dem Heap inkrementieren, egal welcher Thread die Zeile druckt, sollte das Ergebnis ein erhöhter Wert sein. Aber warum druckt es ungeordnet?Java Threading Verwirrung, warum ist die Ausgabe nicht geordnet?

class TestSync implements Runnable{ 
    private int balance; 

    public void run(){ 
    for(int i=0; i<50; i++) { 
     increment(); 
    System.out.println("balance is "+balance); 
    } 
    } 
    public synchronized void increment(){ 
    balance++;  
    } 
} 


public class TestSyncTest { 

    public static void main(String[] args){ 
    TestSync job = new TestSync(); 
    Thread a = new Thread(job); 
    Thread b = new Thread(job); 
    a.start(); 
    b.start(); 
    } 
} 

Ausgang:

balance is 2 
balance is 3 
balance is 4 
balance is 5 
balance is 6 
balance is 7 
balance is 8 
balance is 9 
balance is 10 
balance is 11 
balance is 12 
balance is 13 
balance is 14 
balance is 15 
balance is 16 
balance is 17 
balance is 18 
balance is 19 
balance is 20 
balance is 21 
balance is 22 
balance is 23 
balance is 24 
balance is 2 
balance is 25 
balance is 27 
balance is 28 
balance is 29 
balance is 26 
balance is 30 
balance is 32 
balance is 33 
balance is 34 
balance is 35 
balance is 36 
balance is 37 
balance is 31 
balance is 38 
balance is 40 
balance is 41 
balance is 42 
balance is 43 
balance is 44 
balance is 39 
balance is 46 
balance is 47 
balance is 45 
balance is 49 
balance is 48 
balance is 50 
balance is 52 
balance is 51 
balance is 53 
balance is 55 
balance is 54 
balance is 56 
balance is 58 
balance is 59 
balance is 60 
balance is 57 
balance is 61 
balance is 62 
balance is 63 
balance is 64 
balance is 65 
balance is 66 
balance is 67 
balance is 68 
balance is 69 
balance is 70 
balance is 71 
balance is 72 
balance is 73 
balance is 74 
balance is 75 
balance is 76 
balance is 77 
balance is 78 
balance is 79 
balance is 80 
balance is 81 
balance is 82 
balance is 83 
balance is 84 
balance is 85 
balance is 86 
balance is 87 
balance is 88 
balance is 89 
balance is 90 
balance is 91 
balance is 92 
balance is 93 
balance is 94 
balance is 95 
balance is 96 
balance is 97 
balance is 98 
balance is 99 
balance is 100 
+0

'synchronisiert' verspricht keine Reihenfolge. Es geht mehr um die Sichtbarkeit und Atomizität von Operationen, die verschiedene Threads auf einem Codeblock ausführen. Deshalb. – Tirath

+0

@Tirath [Eine Ausführung ist stark konsistent, wenn die Methodenaufrufe korrekt angeordnet werden können, wobei die gegenseitige Reihenfolge von Aufrufen beibehalten wird, die sich zeitlich nicht überlappen, unabhängig davon, welcher Thread sie aufruft. Mit anderen Worten, bevor die Methode zurückkehrt, wird sie vollständig konsistent mit anderen Threads wirksam.Dies wird auch als "Linearisierbarkeit" oder "Thread-Sicherheit" bezeichnet.] (Http://coldattic.info/shvedsky/pro/blogs/a-foo-walks-into-a-bar/posts/88). Ich denke, das ist, was Java 'synchronisiert' tut. Also, ob die Reihenfolge garantiert ist oder nicht, hängt davon ab, was Sie damit meinen. –

Antwort

2

Das einzige, was synchronisiert wird, ist die Inkrementierung. Das Lesen der Variablen vor dem Drucken ist nicht, und das Drucken selbst wird auf ein anderes Schloss synchronisiert. So könnte man bekommen

  • Thema 1: Erhöhung
  • Gewinde 2: Erhöhung
  • Thema 1: Lesen und Drucken
  • Faden 2: Lesen und Drucken von

BTW, der Rest shared state, und alle die Zugriffe, einschließlich des Lesens des Wertes der Variablen, sollten auf der gleichen Sperre synchronisiert werden.

Ändern Sie den Code zu

class TestSync implements Runnable{ 
    private int balance; 

    public void run(){ 
     for(int i = 0; i < 50; i++) { 
      incrementAndPrint(); 
     } 
    } 

    public synchronized void incrementAndPrint(){ 
     balance++;  
     System.out.println("balance is "+balance); 
    } 
} 
+1

Die PrintIn ist ebenfalls synchronisiert, verwendet aber nicht dieselbe Sperre. –

+0

Also die Anweisung System.out.println ("balance is" + balance); besteht aus 2 Schritten: 1. Lesen Sie den Wert in Balance und 2. drucken Sie es. Ein Thread könnte den Wert gelesen haben, wurde jedoch eingefroren, bevor er gedruckt werden konnte. Wenn er wieder freigegeben wird, druckt er den Wert, der gelesen wurde, bevor er eingefroren wurde. Ist das richtig ? – Anudeep

+0

Es ist eigentlich viel mehr atomare Schritte als das: Lesen Sie den Wert, dann verketten Sie den Wert zu einer Zeichenfolge, die selbst ein neues har-Array erstellen muss, jedes char der ursprünglichen Zeichenfolge in das neue Array kopieren, die ganze Zahl in eine Zeichenfolge umwandeln usw. Zwischen jedem dieser atomaren Schritte kann der Thread vom Scheduler pausiert werden, um einen anderen auszuführen. Aber selbst wenn das Ganze eine einzige atomare Operation ist, da Thread außerhalb des synchronisierten Blocks ausgeführt wird, kann Thread 2 inkrementieren, bevor Thread1 druckt. –

0

synchronisiert nur den Block schützt synchronisiert. Es ist ein häufiger Fehler anzunehmen, dass wenn Sie einen kleinen Thread sicher um Ihr Programm streuen, es tun wird, was Sie wollen. Die Thread-Sicherheit ist eine Art Kette von Operationen, wenn einer nicht synchronisiert ist, kann der Code in beliebiger Reihenfolge ausgeführt werden.

Aber warum druckt es in ungeordneten Weise?

Der Zweck des Threads besteht darin, unabhängige Aufgaben gleichzeitig auszuführen. Daher sollten Sie erwarten, dass es Zeit gibt, wenn der eine oder der andere Thread schneller ausgeführt wird als der andere.

Die eigentliche Frage sollte sein; Warum ist es überhaupt ordentlich?

Dies liegt wahrscheinlich daran, dass ein Thread vor dem anderen beginnt. Wenn ein Thread ausgeführt wird und der andere noch nicht gestartet wurde, ist die Ausgabe "geordnet". Sobald beide Threads ausgeführt werden, können Sie die Rennbedingungen sehen.

0

Sie haben 2 Threads druckt einen Zähler, die App funktioniert ok, wie Sie sehen können, jeder Thread in der Konsole gedruckt wird, sobald der Zählerwert geben wollen, das Aussehen wie durcheinander, weil es nicht einfach ist Art und Weise zu formatieren oder zu identifizieren, welcher Wert aus dem Thread comming ...

einen Namen zu einem jedem Thread hinzufügen und den Namen drucken, so dass Sie die Herkunft der Daten sehen ...

Thread a = new Thread(job, "t1"); 
Thread b = new Thread(job, "t2");