Lost-Update Semaphore

Sonnenblume123

Aktives Mitglied
Hallo,

also die Aufgabe war, welche Werte guthaben annehmen kann, wenn
Konto k = new Konto(); ist. Ich habe da:
11, 22, 33, 31. Nach ein paar Mal ausführen, hab ich da mal 30 gesehen und könnt mir nicht erklären wieso.
Dann kommt Klasse Konto2 ins Spiel, wo ich gefragt wurde, was guthaben für Werte annehmen kann, wenn Konto k = new Konto2(); ist.
Ich dachte nur 31 und 33. Jedoch hab ich gesehen, dass noch 22 und 11 möglich sind. Jetzt dacht ich eigentlich, dass es durch die Semaphore sichergestellt ist, dass nur ein Thread jeweils eine Methode ausführen kann.
Hoffe ihr könnt mir helfen🙂
Code:
import java.util.concurrent.Semaphore;

public class Konto {
    protected double guthaben = 0;

    public void einzahlen(double betrag) {
        guthaben = guthaben + betrag;
       
    }

    public void verzinsen() {
        guthaben = guthaben + (guthaben * 0.1);
       

    }
   
    public double guthaben() {
        return guthaben;
    }
   
    public static void main(String[] args) throws InterruptedException {
        final Konto k = new Konto2();
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                k.einzahlen(10);
                k.verzinsen();
            }
        });
       
        Thread t2 = new Thread(new Runnable() {
            public void run() {
                k.einzahlen(20);
               
               
            }
        });
       
        t1.start(); t2.start();
        t1.join(); t2.join();
        System.out.println("guthaben: " + k.guthaben());
    }
   
    static class Konto2 extends Konto{
        static Semaphore s = new Semaphore(1);
        public void einzahlen(float betrag) {
            try {
                s.acquire();
                guthaben = guthaben + betrag;
                s.release();
            }catch(InterruptedException e) {
                e.printStackTrace();
            }
        }
        public void verzinsen() {
            einzahlen(guthaben * 0.1);
        }
    }
}
 
30.0 ist möglich, nachdem Thread1 10.0 eingezahlt hat und sich jetzt in verzinsen() befindet und Thread2 sich gleichzeitig in einzahlen(20) befindet. Da beide Methoden nicht synchronisiert sind, kann es jetzt passieren, dass Thread2 das Feld `guthaben` liest noch bevor Thread1 innerhalb von verzinsen() das Ergebnis geschrieben hat, und jetzt Thread1 sein Ergebnis innerhalb von verzinsen() in das Feld schreibt, aber das Ergebnis sofort anschließend wieder von Thread2 in seinem einzahlen(20) Aufruf überschrieben wird. Merke: Thread2 glaubte da noch, dass `guthaben` == 10.0 war, weil er `guthaben` gelesen hat, bevor Thread1 in verzinsen() den neuen verzinsten Wert geschrieben hat.
 
Dasselbe Problem besteht übrigens auch mit Konto2, da das Lesen von guthaben in verzinsen() nicht mit dem Schreiben in einzahlen() synchronisiert wird. Und die Ergebnisse 11 und 22 entstehen aus demselben Grund, nur mit unterschiedlicher Reihenfolge vom Lesen und Schreiben von guthaben zwischen Thread1 und Thread2.
 
Könnte ich das Problem für Konto lösen, wenn ich einzahlen synchronized mache?
Nein. Das Problem ist nicht, dass mehrere Threads einzahlen() aufrufen, sondern dass ein Thread einzahlen() aufruft während ein anderer verzinsen() aufruft und die dort getätigten Lese- und Schreiboperationen um das Feld guthaben konkurrieren. Du müsstest schon sowohl einzahlen() als auch verzinsen() als synchronized markieren.
 

Zurück
Oben