2016-08-06 35 views
1

Warum kann es in meinem Code eine Race Condition geben, wenn alle Accounts synchronisiert sind?Wie kann es eine Race Condition geben?

class Transfer implements Runnable { 

    Konto fromAccount; 
    Konto toAccount; 
    Integer amount; 

    public void run() { 
     synchronized (fromAccount) { 
      if (fromAccount.book(-amount)) { 
       toAccount.book(amount); 
      } 
     } 
    } 
} 
public class Main { 
    public static void main(String[] args) throws InterruptedException 

     Account thomas = new Account(1234, 100); 
     Account mathias = new Account(5678, 100); 
     Thread transfer1 = new Thread(new Transfer(80, thomas, mathias)); 
     Thread transfer2 = new Thread(new Transfer(95, mathias, thomas)); 
     transfer1.start(); 
     transfer2.start(); 
     transfer1.join(); 
     transfer2.join(); 
} 

Von meinem Verständnis, abfuhr1 seine fromAccount (Thomas) sperrt und transfer2 sperrt seine fromAccount (mathias), so sollten sie nicht beide enden in einer Sackgasse auf?

+1

Haben Sie einen Deadlock oder eine Racebedingung? Sie erwähnen beides, aber sie sind verschieden. Sieht für mich so aus, als hättest du eine potentielle Rasse, aber nicht einen Stillstand auf einen kurzen Blick. – Brick

Antwort

1

Das Problem ist, dass der Code toAccount.book(amount) nicht mit dem synchronisierten Schutz ausgeführt wird.

Technisch gesehen könnte es passieren, dass thread1 Sperre hält auf thomasAccount und thread2 hält Sperre auf mathiasAccount aber die thread2 läuft noch Buch über thomasAccount zur gleichen Zeit wie die thread1 Buch über thomasAccount verläuft. Dies kann zu Inkonsistenzen führen, da einer der Threads das Ergebnis des zweiten Threads ignorieren kann.

Einfach jeder Thread, der auf einem Konto läuft, muss zuerst das Konto sperren (synchronisieren), egal ob es Plus oder Minus ist.

Um Deadlock zu vermeiden, machen Sie die Konten vergleichbar (oder verwenden Sie eine ID des Accounts) und sperren Sie die Accounts immer in aufsteigender Reihenfolge. Oder Sie können dafür Hash verwenden, aber falls der Hash derselbe ist, benötigen Sie eine globale Sperre.

+0

Danke für die Antwort, aber ich verstehe immer noch nicht eins: Wenn thread1 'thomasAccount' gesperrt, wie kann thread2 Buch auf' thomasAccount' laufen lassen? – Numb3rs

+0

Der Thread2 stoppt nur, wenn er versucht, dieselbe Sperre zu erhalten. Der Aufruf von 'book()' selbst führt das Sperren nicht durch. –

+0

Nochmals vielen Dank, Sie helfen mir wirklich hier! Also, wenn ich das richtig verstehe, kann auf ein gesperrtes Objekt immer noch von anderen Threads zugegriffen werden, zum Beispiel durch einen Methodenaufruf, solange es nicht versucht, eine Sperre dafür zu erhalten. Ist das richtig? – Numb3rs

1

Ihre run Methode synchronisiert nur auf fromAccount, nicht toAccount. Code, der nicht synchronisiert wird, wird nicht durch den synchronisierten Code blockiert; Die beiden Threads, die versuchen, auf etwas zuzugreifen, müssen sowohl synchronisieren, um den Zugriff zu serialisieren.

So Ihre run Methode muss synchronisieren nicht nur auf fromAccount, sondern auch auf toAccount, um für jede Synchronisierung auf toAccount es warten zu lassen.