2016-05-28 8 views
1

Ich frage mich, warum das Ergebnis nicht 400 000 ist. Es gibt zwei Threads, warum wird es blockiert?Java Threading Problem?

class IntCell { 
    private int n = 0; 
    public int getN() {return n;} 
    public void setN(int n) {this.n = n;} 
} 
class Count extends Thread { 
    private static IntCell n = new IntCell(); 
    @Override public void run() { 
     int temp; 
     for (int i = 0; i < 200000; i++) { 
      temp = n.getN(); 
      n.setN(temp + 1); 
     } 
    } 
    public static void main(String[] args) { 
     Count p = new Count(); 
     Count q = new Count(); 
     p.start(); 
     q.start(); 
     try { p.join(); q.join(); } 
     catch (InterruptedException e) { } 
     System.out.println("The value of n is " + n.getN()); 
    } 
} 

Warum gibt es so ein Problem damit?

+3

Was meinst du mit "warum wird es blockiert"? Was ist falsch - stellen Sie sich vor, wenn beide Threads einen bestimmten Wert (sagen wir 100), dann beide inkrementieren (bis 101), dann speichern Sie den Wert. Es sind zwei Inkremente aufgetreten, aber das Ergebnis ist 101 anstelle von 102 ... –

+0

Vielleicht möchten Sie die Ergebnisse hinzufügen, die Sie erhalten. Zu Ihrer Information: Dies ist eine grundlegende Sache, die Sie mit Threading und gleichzeitiger Aktualisierung von Werten verstehen müssen. –

+0

Ich schlage vor, Sie gehen und lesen, wie Multi-Threading funktioniert. Ihre Frage beinhaltet ein ernsthaftes Missverständnis. Multi-threading ist so schwierig, richtig zu machen, dass Sie ein gutes Verständnis haben müssen, bevor Sie beginnen. – davmac

Antwort

2

wenn zwei Threads ein Objekt zur gleichen Zeit zuzugreifen, sie miteinander interferieren, und das Ergebnis ist nicht deterministisch. Stellen Sie sich zum Beispiel vor, dass liest den Wert n und bekommt, sagen wir 0, dann q liest den gleichen Wert und bekommt 0 auch dann setzt Wert auf 1 und q setzt es auch auf 1 (weil es immer noch denkt, dass es hat Wert 0). Jetzt wird der Wert von n um 1 erhöht, obwohl beide Zähler es einmal inkrementiert haben. Sie müssen den Block synchronized verwenden, um sicherzustellen, dass die Zähler sich nicht gegenseitig stören. Siehe https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html für mehr.

6

Da die Art und Weise Sie Ihre Variable erhöhen, in der Tat nicht eine atomare Operation ist es, Ihnen zu erhöhen:

  1. den vorherigen Wert Get
  2. ein In diesem Wert
  3. einen neuen Wert

Sie sind 3 Operationen nicht atomar getan Sie sollten entweder uns eine synchronized Block oder verwenden Sie stattdessen eine AtomicInteger.

Mit einem synchronized Block es so etwas wie wäre:

synchronized (n) { 
    temp = n.getN(); 
    n.setN(temp + 1); 
} 

Mit einem AtomicInteger müssen Sie Ihren Code als nächstes neu zu schreiben:

class IntCell { 
    private final AtomicInteger n = new AtomicInteger(); 
    public int getN() {return n.get();} 
    public void incrementN(int n) {this.n.addAndGet(n);} 
} 

for (int i = 0; i < 200000; i++) { 
    n.incrementN(1); 
} 

Der Ansatz mit einem AtomicInteger blockierungs ist so es wird schneller sein

2

Das Problem hier ist, dass Sie für Race-Bedingungen zulassen. Betrachten Sie den Block in der Schleife:

temp = n.getN(); 
n.setN(temp + 1); 

Der Code Kontextwechsel zwischen der Zeit, die aktuelle N erhalten und durch die Zeit, die Sie erhöhen, so dass Sie einen „alten“ Wert gesetzt. Ein Weg, um dies ist der inneren Teil der Schleifendurchläufe in einem synchronisierten Block, um sicherzustellen:

for (int i = 0; i < 200000; i++) { 
    synchronized (n) {/Here! 
     temp = n.getN(); 
     n.setN(temp + 1); 
    } 
}