Nebenläufige Programmierung mit Threads

Anlegen von Threads

Threads erzeugen

Implementiere ein Thread-Beispiel und erzeuge in main(...) neue 100 Threads. Die run()-Methode jedes Threads soll dabei die Thread-ID in einer Endlosschleife mittels System.out.println(Thread.currentThread().getId()); anzeigen. Wir erhalten dann auf dem Bildschirm Meldungen der Art

1
2
1
2
3
2

Wie viele Threads lassen sich erzeugen, bis das System "steht"? Beobachte den Speicherverbrauch unter dem Task-Manager (Alt/Ctrl/Del) an. Lässt sich abschätzen, was ein Thread "kostet"?

Lösung

Thread-Eigenschaften

Threads schlafen

Das unter Unix bekannte Programm sleep kann man auf der Kommandozeile aufrufen und es schläft dann eine Zeit lang.

  1. Implementiere das Programm in Java, sodass man auf der Kommandozeile schreiben kann:
    $ java Sleep 22
    Dann soll das Programm 22 Sekunden schlafen.
  2. Jetzt sollen nicht nur Sekunden verwendet werden. Erweitere das Programm, sodass ein zweites Argument auch Minuten, Stunden oder Tage wartet. Wirf für die Schreibweise einen Blick in das C-Programm.

Lösung

Dämonen

Implementiere einen java.lang.Thread in einer Endlosschleife. Er soll jede Sekunde eine Meldung wie etwa "Hallo" auf dem Bildschirm ausgeben. Markiere den Thread einmal als Dämon und einmal nicht.

Lösung

Threads beobachten Dateien *

Schreibe eine Klasse DateiBeobachter mit einem Konstruktor DateiBeobachter(String) und DateiBeobachter(java.io.File). Die Klasse soll beobachten, ob sich eine Datei im Dateisystem ändert.

Lösung

Ein Timer-Label *

Klasse Uhrzeit, der Frame
Klasse TimeLabel

Exceptions auffangen (20 Minuten)

Implementiere einen Thread, der durch die Division mit Null eine Exception wirft. Fange den Fehlerfall dieses speziellen Threads durch einen UncaughtExceptionHandler ab.

Lösung

Executor

Thread-Pool (20 Minuten)

Erzeuge mit ExecutorService s = Executors.newCachedThreadPool(); einen ExecutorService und führe mit s.execute(...) Programmcode vom folgenden Runnable aus:

class MyRunnable implements Runnable
{
  public void run()
  {
    System.out.println( Thread.currentThread() );
//  try {
//   Thread.sleep( 1000 );
//  } catch ( InterruptedException e ) {
//   e.printStackTrace();
//  }
  }
}

Das Programm sieht dann so aus:

ExecutorService s = Executors.newCachedThreadPool();
Runnable run = new MyRunnable();
s.execute( run );
s.execute( run );
s.execute( run );
s.execute( run );
s.execute( run );
s.execute( run );
...

Welchen Unterschied macht es, ob man die Zeilen auskommentiert oder wieder in den Programmfluss integriert?

Letzte Modifikation von Webseiten

Folgende Klasse implementiert eine Methode, die einen Zeitstempel zurückgibt, in der eine Webseite zum letzten mal verändert wurde (die Daten können u. U. nicht verfügbar sein, dann steht die Zeit auf dem 1.1.1970).

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class WebChecker {

    public static void main(String[] args) throws IOException {

   	 ZonedDateTime urlLastModified = getLastModified(new URL("http://www.tutego.de/index.html"));
   	 System.out.println(urlLastModified);
   	 ZonedDateTime urlLastModified2 = getLastModified(new URL("http://www.spiegel.de/index.html"));
   	 System.out.println(urlLastModified2);

    }

    private static ZonedDateTime getLastModified(URL url) {
   	 try {
   		 HttpURLConnection con = (HttpURLConnection) url.openConnection();
   		 long dateTime = con.getLastModified();
   		 con.disconnect();
   		 return ZonedDateTime.ofInstant(Instant.ofEpochMilli(dateTime),
                                           ZoneId.of("GMT"));
   	 } catch (IOException e) {
   		 throw new IllegalStateException(e);
   	 }
    }
}

Schreibe eine Klasse PageLastModifiedCallable mit einem Konstruktor, der mit einer URL aufgebaut werden kann. Nutze das Callabe im Thread-Pool.

Callable und das Wetter in NRW

Lege ein Maven-Projekt an, sofern nicht schon geschehen. Füge in der POM eine Dependency auf jsoup ein. Setze zum Testen folgendes in eine main-Methode, um das Wetter von Dortmund zu erfragen:

String url = "http://www.wetter.com/wetter_aktuell/wettervorhersage/16_tagesvorhersage/deutschland/dortmund/DE0002221.html";
Elements select = Jsoup.connect( url ).get().select( ".text--white.beta.palm-hide" );
System.out.println( select.text() ); // 10°C

Implementiere folgendes Callable:

public class WeatherCallable implements Callable<Double> {

  private final String cityUrl;

  public WeatherCallable( String cityUrl ) {
    this.cityUrl = cityUrl;
  }

  @Override
  public Double call() throws Exception {
    // Mit jsoup das Wetter von der Seite holen
    return Double.NaN;
  }
}

Hole mit Hilfe des Executor-Services und einem Future das Wetter in Dortmund.

Wie ist die Durchschnittstemperatur von Dortmund, Bochum und Essen?

Kommt Zeit kommt Runnable

Nutzte den ScheduledExecutorService zum Arbeiten von Programmteilen.

Wie kann man die Abarbeitung etwa nach ein paar Sekunden beenden?

Synchronisation

Motivation für Synchronisierung *

Schreibe ein Runnable, was in einen StringBuffer 100 mal Thread.currentThread().toString() anhängt. Starte drei Threads, die sich alle den gleichen StringBuffer teilen, ihn also als gemeinsame Ressource nutzen. Ändere StringBuffer in StringBuilder und beobachte das Ergebnis.

Lösung

Thread-Kooperation und Benachrichtigung

Fruchtig oder sahnig?

Zwei Threads sollen abwechseld "fruchtig" und "sahnig" auf dem Bildschirm ausgeben. Dazwischen soll es "Denkpausen" von bis zu einer Sekunde geben.

Lösung

Schere, Papier, Stein

Zwei Threads sollen auf ein Signal warten, das von einem dritten Threads jede Sekunde gesendet wird. Kommt das Signal, soll sich der erste und zweite Thread zufällig für Schere, Papier oder Stein entscheiden.

Lesen und Schreiben in einen Puffer

Der Ausdruck new BigInteger(1024, new SecureRandom()) erzeugt eine große Zufallszahl vom Typ BigInteger. Schreibe eine eigene Klasse BigIntegerRandoms mit einer Methode BigInteger nextBigIntegerRandom(), die eine Zufallszahl liefert und direkt einen Hintergrundthread anstößt, der eine neue Zufallszahl berechnet.