Skip to content
Branch: master
Find file History
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
..
Failed to load latest commit information.
README.adoc

README.adoc

Praktikum Concurrency 1

1. Printer-Threads: Verwendung von Java Threads

Nachfolgend einige Basisübungen zum Starten und Stoppen von Threads in Java.

class PrinterThread extends Thread {
    char ch;
    int sleepTime;

    public PrinterThread(String name, char c, int t) {
        super(name);
        ch = c; sleepTime = t;
    }

    public void run() {
        System.out.println( getName() + " run laueft an");
        for (int i = 1; i < 100; i++) {
            System.out.print (ch);
            try {
                Thread.sleep(sleepTime);
            } catch (InterruptedException e) { ; }
        }
        System.out.println( '\n' + getName() + " run  fertig");
    }
}

public class Printer1 {
    public static void main(String[] arg) {
        PrinterThread a = new PrinterThread("PrinterA", '.', 0);
        PrinterThread b = new PrinterThread("PrinterB", '*', 0);
        a.start();
        b.start();
        b.run(); //Wie kann das abgefangen werden?
    }
}
  1. Studieren Sie das Programm Printer1.java: Die Methode Thread.run() ist public und kann daher direkt aufgerufen werden. Erweitern Sie die Methode run() so, dass diese sofort terminiert, wenn sie direkt und nicht vom Thread aufgerufen wird.


    Tip
    Was liefert die Methode Thread.currentThread() zurück?
  2. Schreiben Sie das Programm so um, dass die run-Methode über das Interface Runnable implementiert wird.

    Führen Sie dazu eine Klasse Printer ein, die das Interface Runnable implementiert.
    
Starten Sie zwei Threads, so dass die selbe Ausgabe entsteht wie bei (a).
    
Verwenden Sie den untenstehenden Code für den Test ihrer Implementation:

public class Printer2 {
    public static void main(String[] arg) {
        Printer a = new Printer('.', 0);
        Printer b = new Printer('*', 0);
        Thread t1 = new Thread(a, "PrinterA");
        Thread t2 = new Thread(b, "PrinterB");
        t1.start();
        t2.start();
    }
}
  1. Wie kann man es erreichen, die Fairness zu erhöhen, d.h. dass der Wechsel zwischen den Threads häufiger erfolgt? Wirkt es sich aufs Resultat aus?

  2. Wie muss man das Hauptprogramm anpassen, damit der Main-Thread immer als letztes endet?

2. Konto-Übertrag

Nachfolgend eine einfache Klasse, um ein Konto zu verwalten, den Saldo abzufragen oder zu aktualisieren.

class Account {
    private int id;
    private int saldo = 0;

    public Account(int id, int initialSaldo) {
        this.id = id;

        this.saldo = initialSaldo;
    }
    public int getId() {
        return id;
    }
    public int getSaldo () {
        return saldo;
    }
    public void changeSaldo (int delta) {
        this.saldo += delta;
    }
}

Ein Entwickler implementiert aufbauend auf der Klasse Account eine Operation für den Transfer eines Geldbetrages zwischen zwei Konti. Die Klasse AccountTransferThread implementiert dazu die Methode accountTransfer, welche in einer Schleife mehrfach aufgerufen wird, um viele kleine Transaktionen zu simulieren. Das Testprogramm AccountTransferTest (siehe abgegebenen Code) erzeugt schlussendlich mehrere Threads, die teilweise auf denselben Konto-Objekten operieren.

class AccountTransferThread extends Thread {
    private Account fromAccount, toAccount;
    private int amount;
    public AccountTransferThread (Account fromAccount,
                                  Account toAccount,
                                  int amount) {
        this.fromAccount = fromAccount;
        this.toAccount = toAccount;
        this.amount = amount;
    }

    public void accountTransfer() {
        //do not overdraw account
        if (fromAccount.getSaldo() > amount) {
            fromAccount.changeSaldo(-betrag);
            toAccount.changeSaldo(betrag);
        }
    }

    public void run() {
        for (int i=0; i<10000; i++) {
            accountTransfer();
        }
    }
}
  1. Was stellen Sie fest, wenn Sie das Testprogramm laufen lassen?
 Erklären Sie wie die Abweichungen zustande kommen.

  2. Im Unterricht haben Sie gelernt, dass sie kritische Bereiche Ihres Codes durch Mutual-Exclusion geschützt werden sollen. Wie macht man das in Java?

    Versuchen Sie mit Hilfe von Mutual-Exclusion sicher zu stellen, dass keine Abweichungen entstehen. Reicht es, wenn Sie die kritischen Methoden in Account schützen?

    Untersuchen Sie mehrere Varianten von Locks (Lock auf Methode oder Block, Lock auf Instanz oder Klasse).

    Ihre Implementierung muss noch nebenläufige Transaktionen erlauben, d.h. wenn Sie zu stark synchronisieren, werden alle Transaktionen in Serie ausgeführt und Threads machen keinen Sinn mehr.
    
Stellen Sie für sich folgende Fragen:

    • Welches ist das Monitor-Objekt?

    • Braucht es eventuell das Lock von mehr als einen Monitor während der Transaktion?

  3. (optional) Wenn Sie es geschafft haben die Transaktion thread-safe zu implementieren, ersetzen Sie in AccountTransferTest die die folgende Zeile
:
    AccountTransferThread t1 =
new AccountTransferThread("Worker 1", account3, account1, 1);

    durch
    
AccountTransferThread t1 =
new AccountTransferThread("Worker 1", account1, account3, 1);
    
und starten Sie das Programm noch einmal. Was stellen Sie fest? (evtl. müssen Sie es mehrfach versuchen, damit der Effekt auftritt).
    Was könnte die Ursache sein und wie können Sie es beheben?

3. Traffic Light

In dieser Aufgabe sollen Sie die Funktionsweise einer Ampel und deren Nutzung nachahmen. Benutzen Sie hierzu die Vorgabe TrafficLightOperation.java.

Note
Der Einfachheit halber sind alle Klassen in einer Datei zusammengefasst. Natürlich steht es ihnen frei, die Klassen auf mehrere Dateien aufzuteilen.
  1. Erweitern Sie zunächst eine Klasse TrafficLight mit drei Methoden:

    • Eine Methode zum Setzen der Ampel auf „rot“.

    • Eine Methode zum Setzen der Ampel auf „grün“.

    • Eine Methode mit dem Namen passby()`. Diese Methode soll das Vorbeifahren eines Fahrzeugs an dieser Ampel nachbilden: Ist die Ampel rot, so wird der aufrufende Thread angehalten, und zwar so lange, bis die Ampel grün wird. Ist die Ampel dagegen grün, so kann der Thread sofort aus der Methode zurückkehren, ohne den Zustand der Ampel zu verändern. Verwenden Sie wait, notify und notifyAll nur an den unbedingt nötigen Stellen!

Note
Die Zwischenphase „gelb“ spielt keine Rolle – Sie können von diesem Zustand abstrahieren!
  1. Erweitern Sie nun die Klasse Car (abgeleitet von Thread). Im Konstruktor wird eine Referenz auf ein Feld von Ampeln übergeben. Diese Referenz wird in einem entsprechenden Attribut der Klasse Car gespeichert. In der run-Methode werden alle Ampeln dieses Feldes passiert, und zwar in einer Endlosschleife (d.h. nach dem Passieren der letzten Ampel des Feldes wird wieder die erste Ampel im Feld passiert). Natürlich darf das Auto erst dann eine Ampel passieren, wenn diese auf grün ist!
    Für die Simulation der Zeitspanne fürs Passieren können Sie die folgende Anweisung verwenden: sleep((int)(Math.random() * 500));

Beantworten Sie entweder (c) oder (d) (nicht beide):

  1. Falls Sie bei der Implementierung der Klasse TrafficLight die Methode notifyAll benutzt haben: Hätten Sie statt notifyAll auch die Methode notify verwenden können, oder haben Sie notifyAll unbedingt gebraucht? 
Begründen Sie Ihre Antwort!

  2. Falls Sie bei der Implementierung der Klasse Ampel die Methode notify benutzt haben: Begründen Sie, warum Sie notifyAll nicht unbedingt gebraucht haben!

  3. Testen Sie das Programm TrafficLightOperation.java. Die vorgegebene Klasse TrafficLightOperation implementiert eine primitive Simulation von Autos, welche die Ampeln passieren. Studieren Sie den Code dieser Klasse und überprüfen Sie, ob die erzeugte Ausgabe sinnvoll ist.

4. Thread Priority (optional)

Diese Aufgabe vermittelt einen Einblick in das Prioritätensystem der Java Threads. Zur Aufgabe gehört die vorgegebene Klasse PriorityTest.java. Diese muss für Teile der Aufgabe modifiziert werden. Mit Hilfe der Klasse soll das Verhalten von Java Threads mit verschiedenen Prioritäten analysiert werden.

Tip
Es kann sein, dass verschiedene Betriebssysteme und Java-Versionen sich unterschiedlich verhalten http://www.javamex.com/tutorials/threads/priority.shtml

Je nach Priorität im Bereich von Thread.MIN_PRIORITY=1 über Thread.NORM_PRIORITY=5 bis Thread.MAX_PRIORITY=10, sollte der Thread vom Scheduler bevorzugt behandelt werden, d.h. der Zähler count sollte häufiger inkrementiert werden.

Folgende Fragen müssen abgeklärt und beantwortet werden:

  1. Wie verhält es sich, wenn alle Threads die gleiche Priorität haben?

  2. Was stellen Sie fest, wenn die Threads unterschiedliche Priorität haben?
    Erhöhen Sie auch die Anzahl Threads (z.B. 100), um eine Ressourcen-Knappheit zu provozieren.

You can’t perform that action at this time.