1. Thread-Übersicht
Threads sind die grundlegende Ausführungseinheit für die Programmausführung. Wenn das Betriebssystem (mit Ausnahme von Single-Thread-Betriebssystemen wie dem frühen DOS von Microsoft) ein Programm ausführt, wird im System ein Prozess eingerichtet, und in diesem Prozess muss mindestens ein Thread eingerichtet werden (dieser Thread wird als Hauptthread bezeichnet). Thread) als Einstiegspunkt für die Ausführung dieses Programms. Daher verfügt jedes im Betriebssystem laufende Programm über mindestens einen Hauptthread.
Prozesse und Threads sind zwei wesentliche Betriebsmodelle in modernen Betriebssystemen. Es kann mehrere Prozesse im Betriebssystem geben, darunter Systemprozesse (vom Betriebssystem intern erstellte Prozesse) und Benutzerprozesse (von Benutzerprogrammen erstellte Prozesse). Ein Prozess kann einen oder mehrere Threads haben. Es gibt keinen gemeinsamen Speicher zwischen Prozessen, was bedeutet, dass die Prozesse im System in ihren eigenen unabhängigen Speicherbereichen ausgeführt werden. Threads in einem Prozess können den vom System dem Prozess zugewiesenen Speicherplatz gemeinsam nutzen.
Threads können nicht nur den Speicher des Prozesses teilen, sondern verfügen auch über einen eigenen Speicherplatz, der auch als Thread-Stack bezeichnet wird. Er wird hauptsächlich zum Speichern des Threads verwendet Daten, die innerhalb des Threads verwendet werden, z. B. Thread-Stack-Variablen, die in der Ausführungsfunktion definiert sind.
Hinweis: Jeder Thread führt beim Erstellen eine Funktion aus. Diese Funktion wird als Thread-Ausführungsfunktion bezeichnet. Diese Funktion kann auch als Einstiegspunkt des Threads betrachtet werden (ähnlich der Hauptfunktion im Programm). Unabhängig davon, welche Sprache oder Technologie zum Erstellen eines Threads verwendet wird, muss diese Funktion ausgeführt werden (der Ausdruck dieser Funktion kann unterschiedlich sein, aber es wird immer eine solche Funktion geben). Beispielsweise ist der dritte Parameter der API-Funktion CreateThread, der zum Erstellen eines Threads in Windows verwendet wird, der Zeiger dieser Ausführungsfunktion.
Nachdem das Betriebssystem den Prozess in mehrere Threads unterteilt hat, können diese Threads gleichzeitig unter der Verwaltung des Betriebssystems ausgeführt werden, wodurch die Laufeffizienz des Programms erheblich verbessert wird. Obwohl die Ausführung von Threads aus Makroperspektive so aussieht, als würden mehrere Threads gleichzeitig ausgeführt, handelt es sich tatsächlich nur um eine Verschleierung des Betriebssystems. Da eine CPU jeweils nur eine Anweisung ausführen kann, ist es unmöglich, auf einem Computer mit einer CPU zwei Aufgaben gleichzeitig auszuführen. Um die Laufeffizienz des Programms zu verbessern, entfernt das Betriebssystem einen Thread, wenn er inaktiv ist, und lässt ihn von anderen Threads ausführen. Diese Methode wird Thread-Planung genannt. Der Grund dafür, dass an der Oberfläche mehrere Threads gleichzeitig ausgeführt werden, liegt darin, dass die Zeit zum Wechseln zwischen verschiedenen Threads sehr kurz ist und unter normalen Umständen sehr häufig gewechselt wird. Angenommen, wir haben die Threads A und B. Zur Laufzeit kann es sein, dass, nachdem A eine Millisekunde lang ausgeführt wurde, B nach dem Wechsel zu B eine weitere Millisekunde lang ausgeführt wird, dann zu A wechselt und A eine weitere Millisekunde lang ausgeführt wird. Da 1 Millisekunde für normale Menschen schwer wahrnehmbar ist, sieht es oberflächlich so aus, als würden A und B gleichzeitig ausgeführt, tatsächlich werden A und B jedoch abwechselnd ausgeführt.
2. Die Vorteile, die Threads für uns bringen
Durch die richtige Verwendung von Threads können Entwicklungs- und Wartungskosten gesenkt und sogar die Leistung komplexer Anwendungen verbessert werden. Beispielsweise können in GUI-Anwendungen Ereignisse durch die asynchrone Natur von Threads besser verarbeitet werden; in Anwendungsserverprogrammen können mehrere Threads zur Bearbeitung von Client-Anfragen eingerichtet werden. Threads können sogar die Implementierung virtueller Maschinen vereinfachen. Beispielsweise läuft der Garbage Collector der Java Virtual Machine (JVM) normalerweise in einem oder mehreren Threads. Daher wird die Verwendung von Threads unsere Anwendungen in den folgenden fünf Aspekten verbessern:
1. Nutzen Sie die CPU-Ressourcen voll aus
Die meisten Computer auf der Welt verfügen mittlerweile nur noch über eine CPU. Daher ist es besonders wichtig, die CPU-Ressourcen voll auszunutzen. Beim Ausführen eines Single-Threaded-Programms kann die CPU im Leerlauf sein, während das Programm blockiert ist. Dies führt zu einer großen Verschwendung von Rechenressourcen. Durch die Verwendung von Multithreading in einem Programm können andere Threads ausgeführt werden, wenn ein Thread schläft oder blockiert ist und die CPU zufällig im Leerlauf ist. Auf diese Weise ist es für die CPU schwierig, im Leerlauf zu sein. Daher werden die CPU-Ressourcen vollständig ausgenutzt.
2. Vereinfachen Sie das Programmiermodell
Wenn das Programm nur eine Aufgabe ausführt, schreiben Sie einfach ein Single-Thread-Programm und schreiben Sie den Code gemäß den Schritten zur Ausführung der Aufgabe. Um aber mehrere Aufgaben zu erledigen, muss man, wenn man immer noch einen einzelnen Thread nutzt, im Programm festlegen, ob und wann jede Aufgabe ausgeführt werden soll. Es zeigt beispielsweise die Stunden, Minuten und Sekunden einer Uhr an. Wenn Sie einen einzelnen Thread verwenden, müssen Sie die Rotationszeit und den Winkel dieser drei Zeiger nacheinander in der Schleife beurteilen. Wenn drei Threads für die Anzeige dieser drei Zeiger verwendet werden, bedeutet dies, dass für jeden Thread eine separate Aufgabe ausgeführt wird. Dies hilft Entwicklern, das Programm zu verstehen und zu warten.
3. Vereinfachen Sie die Verarbeitung asynchroner Ereignisse
Wenn eine Serveranwendung unterschiedliche Clientverbindungen empfängt, besteht die einfachste Möglichkeit, damit umzugehen, darin, für jede Clientverbindung einen Thread zu erstellen. Der Listening-Thread ist dann weiterhin dafür verantwortlich, auf Anfragen des Clients zu lauschen. Wenn diese Art von Anwendung von einem einzelnen Thread verarbeitet wird und der abhörende Thread eine Clientanforderung empfängt, beginnt er, die vom Client gesendeten Daten zu lesen. Nach dem Lesen der Daten wird die Lesemethode, dh dieser Thread, blockiert wird Clientanfragen nicht mehr abhören können. Wenn Sie mehrere Clientanforderungen in einem einzelnen Thread verarbeiten möchten, müssen Sie nicht blockierende Socket-Verbindungen und asynchrone E/A verwenden. Allerdings ist die Verwendung asynchroner E/A schwieriger zu steuern und fehleranfälliger als die Verwendung synchroner E/A. Daher können asynchrone Ereignisse wie mehrere Anfragen mithilfe von Multithreading und synchroner E/A einfacher verarbeitet werden.
4. Machen Sie die GUI effizienter
Wenn ein einzelner Thread zum Verarbeiten von GUI-Ereignissen verwendet wird, muss eine Schleife verwendet werden, um nach GUI-Ereignissen zu suchen, die jederzeit auftreten können. Innerhalb der Schleife müssen zusätzlich zur Suche nach GUI-Ereignissen andere Programmcodes ausgeführt werden. Wenn der Code zu lang ist, werden die GUI-Ereignisse „eingefroren“, bis der Code ausgeführt wird.
In modernen GUI-Frameworks (wie SWING, AWT und SWT) wird ein separater Event Dispatch Thread (EDT) zum Scannen von GUI-Ereignissen verwendet. Wenn wir eine Schaltfläche drücken, wird die Click-Event-Funktion der Schaltfläche in diesem Event-Dispatch-Thread aufgerufen. Da die Aufgabe des EDT lediglich darin besteht, GUI-Ereignisse zu scannen, ist die Reaktion auf Ereignisse auf diese Weise sehr schnell.
5. Kosteneinsparungen
Im Allgemeinen gibt es drei Methoden, um die Effizienz der Programmausführung zu verbessern:
(1) Erhöhen Sie die Anzahl der CPUs im Computer.
(2) Starten Sie mehrere Prozesse für ein Programm
(3) Verwenden Sie mehrere Prozesse im Programm.
Die erste Methode ist die einfachste, aber auch die teuerste. Diese Methode erfordert keine Änderung des Programms. Theoretisch kann jedes Programm diese Methode verwenden, um die Ausführungseffizienz zu verbessern. Obwohl für die zweite Methode keine neue Hardware erforderlich ist, ist die gemeinsame Nutzung von Daten nicht einfach. Wenn die von diesem Programm auszuführende Aufgabe die gemeinsame Nutzung von Daten erfordert, ist diese Methode nicht praktisch und das Starten mehrerer Threads verbraucht viel System Ressourcen. Die dritte Methode gleicht lediglich die Mängel der ersten Methode aus und übernimmt gleichzeitig deren Vorteile. Mit anderen Worten, es besteht keine Notwendigkeit, eine CPU zu kaufen, und es wird auch nicht viele Systemressourcen beanspruchen, indem zu viele Threads gestartet werden (standardmäßig ist der von einem Thread belegte Speicherplatz viel kleiner als der von einem Prozess belegte Speicherplatz). . Multiple) und Multithreading können den Betriebsmodus mehrerer CPUs simulieren. Daher ist die Verwendung von Multithreading die kostengünstigste Möglichkeit, die Effizienz der Programmausführung zu verbessern.
3. Java-Thread-Modell
Da Java eine rein objektorientierte Sprache ist, ist auch das Threading-Modell von Java objektorientiert. Java kapselt alle notwendigen Funktionen von Threads über die Thread-Klasse. Um einen Thread zu erstellen, muss eine Thread-Ausführungsfunktion vorhanden sein. Diese Thread-Ausführungsfunktion entspricht der Ausführungsmethode der Thread-Klasse. Die Thread-Klasse verfügt außerdem über eine Startmethode, die für die Erstellung eines Threads verantwortlich ist. Dies entspricht dem Aufruf der Windows-Thread-Erstellungsfunktion CreateThread. Wenn die Startmethode aufgerufen wird und der Thread erfolgreich eingerichtet wurde, wird automatisch die Ausführungsmethode der Thread-Klasse aufgerufen. Daher kann jede Java-Klasse, die Thread erbt, über die Startmethode der Thread-Klasse einen Thread erstellen. Wenn Sie Ihre eigene Thread-Ausführungsfunktion ausführen möchten, müssen Sie die Ausführungsmethode der Thread-Klasse überschreiben.
Zusätzlich zur Thread-Klasse im Java-Thread-Modell gibt es auch eine Schnittstelle Runnable, die angibt, ob eine Java-Klasse als Thread-Klasse verwendet werden kann. Diese Schnittstelle verfügt nur über eine abstrakte Methodenausführung, nämlich die Thread-Ausführungsfunktion von Java Thread-Modell. Daher ist das einzige Kriterium für eine Thread-Klasse, ob die Klasse die Ausführungsmethode der Runnable-Schnittstelle implementiert. Mit anderen Worten, eine Klasse mit einer Thread-Ausführungsfunktion ist eine Thread-Klasse.
Wie aus dem Obigen hervorgeht, gibt es zwei Möglichkeiten, Threads in Java zu erstellen. Die andere besteht darin, die Runnable-Schnittstelle zu implementieren und Threads über Thread und die Klasse zu erstellen, die Runnable implementiert. Bei diesen beiden Methoden handelt es sich im Wesentlichen um eine Methode, das heißt, der Thread wird über die Thread-Klasse erstellt und die Ausführungsmethode wird ausgeführt. Ihr großer Unterschied besteht jedoch darin, dass Threads durch Erben der Thread-Klasse erstellt werden. Da Java keine Mehrfachvererbung unterstützt, kann diese Thread-Klasse jedoch keine anderen Klassen erben Methoden zum Einrichten von Threads durch Implementierung der Runnable-Schnittstelle, sodass Thread-Klassen bei Bedarf geschäftsbezogene Klassen anstelle der Thread-Klasse erben können.