Was sind eigentlich Threads?

Seit es Computer gibt, gibt es das Bedürfnis, mehrere Prozesse gleichzeitig durchzuführen, denn man muss sich einfach nur mal vorstellen, wie es aussehen würde, wenn es das nicht geben würde: Du bewegst deine Maus und der Bildschirm geht aus. Du willst etwas Drucken und deine Tastatur blockiert. Du möchtest einen Text schreiben, aber jetzt geht dein Browser nicht mehr. Das ist nicht akzeptabel.

Hier spricht man noch nicht von Multithreading, aber das Konzept von mehreren nebenläufigen Aufgaben ist bereits gegeben.

📖 Nebenläufigkeit
… oder auch “concurrency” ist nicht das Gleiche wie Parallelität, auch wenn viele Artikel diese beiden Begriffe synonym verwenden. Wenn zwei Prozesse nebenläufig sind, bedeutet das eher, dass sie das Potential haben, gleichzeitig zu laufen. In der Praxis wird dieser Effekt aber über geschicktes Scheduling auf nur einem Rechenkern simuliert. Wirklich parallele Prozesse laufen hingegen tatsächlich gleichzeitig.

Single-CPU Scheduling

Sowohl Prozesse des Betriebssystems (Operating System) als auch OS-Threads sind Aufgaben, die zumindest scheinbar gleichzeitig ablaufen. Nehmen wir einen alten Computer mit nur einem Prozessor: Er muss gleichzeitig ein Programm ausführen und Tastatureingaben verarbeiten, kann aber, aufgrund des einen Kernes, nur eine Aufgabe zur Zeit erledigen.

Die Lösung: Das Betriebssystem teilt dem Prozessor die Rechenzeit in kleine Abschnitte ein und lässt die Aufgaben abwechselnd laufen. Zwar passiert das nicht wirklich gleichzeitig (parallel), aber durch das schnelle Umschalten merken wir als Nutzer keinen Unterschied und nehmen die Prozesse als nebenläufig wahr. Man spricht hier von Kontextwechseln (engl. “context switches”) zwischen den unterschiedlichen Programmen.

Der Teil des Betriebssystems, welcher für diese Aufgaben zuständig ist, nennt sich “Scheduler”. Er muss priorisieren, welcher Prozess wie viel Rechenleistung braucht, Teile des Programmspeichers austauschen, wenn der aktive Prozess gewechselt wird und das Programm genau da weiterlaufen lassen, wo es aufgehört hat. Das Programm selber sollte nämlich gar nicht wissen, dass es kurz pausiert, oder wieder gestartet wird.

OS-Prozesse vs. OS-Threads

Der Unterschied zwischen Prozessen und Threads ist rein konzeptionell nur die “Ebene”. Threads sind, vereinfacht erklärt, also verschiedene kleinere Prozesse in einem regulären Prozess. Sie werden, genau wie Prozesse, sowohl nebenläufig als auch parallel vom OS-Scheduler verwaltet.

Was man sich hier primär vor Augen führen sollte:

  • Jedes Programm hat mindestens einen Hauptthread (Mainthread).
  • Auf jeweils einem Rechenkern können Prozesse und Threads Aufgaben nebenläufig erledigen.
  • Bei einem Multi-Core-System (mehrere Rechenkerne), können Prozesse und Threads Aufgaben parallel erledigen; der Grad der Parallelität ist jedoch immer durch die Anzahl der Kerne limitiert.
    (Bei vier Kernen, kann man höchstens 4 Aufgaben gleichzeitig durchführen)

Threads sind also die leichtgewichtigeren Äquivalente von Prozessen. Relevante Unterschiede sind zum Beispiel:

OS-Prozesse OS-Threads
Haben eigenen Adressraum (RAM‑Speicherbereich). Leben innerhalb eines Prozesses und teilen sich dessen Adressraum.
Datenaustausch zwischen Prozessen ist komplex und benötigt ein serialisiertes Zwischenformat Datenaustausch zwischen Threads ist einfach über gemeinsame Variablen möglich.
Kontextwechsel zwischen Prozessen sind extrem zeitaufwändig. Kontextwechsel sind vergleichsweise günstiger, aber immer noch zeitaufwändig.

Eigenschaften von Threads

Ein Programm (Prozess) kann selbst mehrere Threads haben und davon auch deutlich mehr, als es Rechenkerne gibt, denn der Scheduler kümmert sich auch auf einfachen Maschinen darum, dass zwischen Threads nebenläufig hin- und her getauscht wird. Jedoch ist vor allem eine Sache wichtig, die häufig missverstanden wird:

Solange jeder Thread etwas zutun hat, also seine zugewiesene CPU-Zeit nutzt, wird ein Programm nicht schneller, wenn man mehr Threads verwendet, da sie nicht wirklich parallel arbeiten.

Blocking

Wenn Threads sog. IO-Operationen durchführen, also Input und Output, dann müssen sie manchmal warten. Dies passiert zum Beispiel während:

  • eine Datei von der Festplatte geladen wird
  • eine Datenbankabfrage verarbeitet wird
  • eine Netzwerkanfrage (z.B. über HTTP) versendet wird
  • Hardwareeingaben getätigt werden (Maus, Tastatur, Bluetooth-Verbindung)
  • man explizit auf andere Threads wartet

In diesem Zustand “blockiert” der Thread. Er macht nichts, beansprucht zwar auch keine Rechenleistung, aber liegt trotzdem im Speicher. Wenn das Programm in diesem Zustand trotzdem etwas machen möchte, muss es ein anderer Thread machen, welcher ebenfalls Ressourcen kostet. Threads sollten also nach Möglichkeit so wenig/kurz wie möglich blockieren, damit man, im Umkehrschluss, so wenige wie möglich starten muss.

Daemon Threads

Wie lange läuft eigentlich ein Programm? Bis der Hauptthread fertig ist? Was passiert, wenn der Hauptthread einen Thread startet, welcher endlos weiterläuft, obwohl der Hauptthread schon all seine Aufgaben erledigt hat?

Es gibt auf diese Frage zwei Antworten: Entweder, der Hauptthread schließt den zweiten Thread, wenn er (der Hauptthread) fertig ist, oder er läuft so lange weiter, bis der Nebenthread seine Aufgaben erledigt hat. Der zweite Fall ist der “normale”: Wir wünschen uns meistens, dass alle Threads brav ihre Aufgaben erledigen und dann erst terminieren. Nebenthreads, die man zu Programmende sorgenlos abschießen, also terminieren kann, da sie meistens nur verwaltende Aufgaben, wie das Aufräumen von Speicher haben, nennt man “Daemon Threads”.

Ein Programm terminiert also spätestens dann, wenn es nur noch Daemon Threads gibt.

Zwischenfazit

Wir haben jetzt grundlegend etabliert, was OS-Threads sind, wie sie sich von OS-Prozessen unterscheiden und was ein Scheduler macht. Außerdem haben wir den immens wichtigen Unterschied zwischen echter Parallelität und simulierter Nebenläufigkeit geklärt. Letztlich wurden noch zwei wichtige Eigenarten von Threads, nämlich Blocking und der Daemon-Status erklärt; Themen auf die wir später noch weiter eigehen.

Damit sind die wichtigsten Grundlagen geschaffen, um zum Grundthema der Artikelserie zu kommen:
Javas Platformthreads (Nächster Artikel).