1. Verfahren und Fäden
1. Was ist der Prozess?
Enge Definition: Ein Prozess ist eine Instanz eines Computerprogramms, das ausgeführt wird.
Allgemeine Definition: Ein Prozess ist eine laufende Aktivität eines Programms mit bestimmten unabhängigen Funktionen bezüglich eines bestimmten Datensatzes. Es ist die grundlegende Einheit der dynamischen Ausführung des Betriebssystems. In herkömmlichen Betriebssystemen sind Prozesse sowohl grundlegende Zuordnungseinheiten als auch grundlegende Ausführungseinheiten.
2. Was ist ein Thread?
Themen, die manchmal als leichte Prozesse (LWP) bezeichnet werden, sind die kleinsten Einheiten des Programmausführungsflusss. Ein Standard -Thread besteht aus einer Thread -ID, einem aktuellen Befehlszeiger (PC), einer Reihe von Registern und einem Stapel. Darüber hinaus ist ein Thread eine Entität im Prozess und die grundlegende Einheit, die vom System unabhängig geplant und entsandt wird. Der Thread selbst besitzt die Systemressourcen nicht, hat aber nur einige wesentliche Ressourcen während des Betriebs, kann jedoch alle Ressourcen, die dem Prozess gehören, mit anderen Threads zum gleichen Prozess teilen.
3. Was ist der Unterschied zwischen einem Prozess und einem Thread?
Der Hauptunterschied zwischen Prozessen und Threads besteht darin, dass es sich um unterschiedliche Methoden zur Management von Betriebssystemressourcen handelt.
Ein Prozess hat einen unabhängigen Adressraum. Nach dem Absturz eines Prozesses wirkt sich dies nicht auf andere Prozesse im geschützten Modus aus, und ein Thread ist in einem Prozess nur ein anderer Ausführungspfad.
Themen haben ihren eigenen Stapel und lokalen Variablen, aber es gibt keinen separaten Adressraum zwischen Threads. Wenn ein Faden stirbt, bedeutet dies, dass der gesamte Prozess stirbt. Daher sind Multi-Process-Programme robuster als Multi-Thread-Programme, aber beim Wechsel von Prozessen konsumieren sie mehr Ressourcen und sind weniger effizient. Für einige gleichzeitige Operationen, die gleichzeitige Vorgänge erfordern, die bestimmte Variablen gemeinsam nutzen müssen, können sie nur Threads und keine Prozesse verwenden.
Kurz gesagt, der Unterschied zwischen einem Faden und einem Prozess beträgt:
(1) Ein Programm hat mindestens einen Prozess und ein Prozess hat mindestens einen Thread;
.
(3) Der Prozess hat unabhängige Speichereinheiten während der Ausführung und mehrere Threads teilen Speicher, was die Betriebseffizienz des Programms erheblich verbessert.
(4) Es gibt einen Unterschied zwischen Threads und Prozessen während der Ausführung. Jeder unabhängige Thread verfügt über einen Eintrag für die Programmausführung, eine Folge der Ausführung und einen Ausgang für das Programm. Threads können jedoch nicht unabhängig ausgeführt werden und müssen in der Anwendung vorhanden sein, und mehrere Thread -Ausführungssteuerungen werden von der Anwendung bereitgestellt.
(5) Aus logischer Sicht liegt die Bedeutung von Multi-Threading darin, dass in einer Anwendung mehrere Ausführungsteile gleichzeitig ausgeführt werden können. Das Betriebssystem betrachtet jedoch nicht mehrere Threads als mehrere unabhängige Anwendungen, um die Prozessplanung, Verwaltung und Ressourcenzuweisung zu realisieren.
Dies ist der wichtige Unterschied zwischen einem Prozess und einem Thread.
2. Der Lebenszyklus eines Fadens und der fünf Grundzustände
Java -Threads haben fünf Grundzustände:
(1) neuer Zustand (neu): Wenn das Thread -Objektpaar erstellt wird, tritt der neue Zustand ein, wie z. B. Thread t = new MyThread ();
(2) Ready State (Runnable): Wenn die Start () -Methode des Thread -Objekts (t.Start ();), tritt der Thread in den Bereitschaftszustand ein. Ein Thread im rede Zustand bedeutet nur, dass der Thread fertig ist und darauf wartet, dass die CPU die Ausführung jederzeit plant, und nicht, dass der Thread unmittelbar nach T.Start () ausgeführt wird.
, Hinweis: Der Ready -Status ist der einzige Eintrag in den laufenden Status, dh wenn ein Thread in den ausführenden Zustand eingeben möchte, muss er zunächst im vorbereitenden Zustand sein.
(4) Blockierter Zustand: Aus irgendeinem Grund gibt ein Thread im laufenden Zustand die Verwendung der CPU vorübergehend auf und stoppt die Ausführung. Zu diesem Zeitpunkt tritt es in den Blockierstaat ein. Es wird nicht die Chance haben, von der CPU erneut aufgerufen zu werden, um in den laufenden Zustand einzugeben. Nach den Gründen des Blockierens können Blockierungszustände in drei Typen unterteilt werden:
① Warten auf die Blockierung: Der Thread im laufenden Status führt die Wait () -Methode aus, damit der Thread das Warten auf den Blockieren eingeht.
② Synchronisierte Blockierung: Der Thread erfasst das synchronisierte Synchronisationsschloss (weil das Schloss von anderen Fäden besetzt ist) und tritt in den synchronisierten Blockierungszustand ein.
③OTHER Blockierung: Wenn Sie den Sleep () oder Join () des Threads aufrufen oder eine E/A -Anforderung senden, wird der Thread in einen Blockierstatus eingetragen. Wenn der Sleep () -State zeitlich abgestimmt war, Join () darauf wartete, dass der Faden endet oder zeitlich abgestimmt hat oder die E/A-Verarbeitung abgeschlossen wurde, wurde der Thread wieder in den bereiten Zustand eingegeben.
(5) Dead State: Der Thread hat die Ausführung der Ausnahme ausgesetzt oder beendet
3. Implementierung von Java Multithreading
In Java müssen Sie sich auf die Hauptklasse eines Threads verlassen (wie das Konzept einer Hauptklasse, die die Hauptklasse eines Threads darstellt), wenn Sie ein Multi-Thread-Programm implementieren möchten, aber die Hauptklasse dieses Threads muss bei der Definition einige besondere Anforderungen haben. Diese Klasse kann die Thread -Klasse erben oder die Runnable -Schnittstelle implementieren, um die Definition zu vervollständigen.
1. Erben Sie die Thread-Klasse, um Multi-Threading zu implementieren
java.lang.thread ist eine Klasse, die für Threadoperationen verantwortlich ist. Jede Klasse kann zur Hauptklasse eines Threads werden, wenn er die Thread -Klasse erbt. Da es sich um die Hauptklasse handelt, muss es ihre Verwendungsmethoden haben und die vom Thread gestartete Hauptmethode muss die Run () -Methode in der Thread -Klasse überschreiben.
Definieren Sie die Körperklasse eines Threads:
Klasse MyThread erweitert Thread {// Die Hauptklasse des privaten String -Titels des Threads; public myThread (String title) {this.title = title; } @Override public void run () {// Die Hauptmethode des Threads für (int x = 0; x <10; x ++) {System.out.println (this.title + "run, x =" + x); }}}Nachdem es eine Thread -Klasse gibt und entsprechende Betriebsmethoden enthalten sind, sollte das Objekt generiert werden und die Methoden im Inneren aufgerufen werden, sodass das folgende Programm geschrieben wurde:
public class testDemo {public static void main (String [] args) {mythead mt1 = new MyThread ("Thread a"); Mythead mt2 = new MyThread ("Thread B"); Mythead mt3 = new MyThread ("Thread C"); mt1.run (); mt2.run (); mt3.run (); }Auslaufergebnisse:
Faden A läuft, x = 0
Fädeln a läuft, x = 1
Fädeln a läuft, x = 2
Fädeln a läuft, x = 3
Fädeln a läuft, x = 4
Fädeln a läuft, x = 5
Fädeln a läuft, x = 6
Fädeln a läuft, x = 7
Fädeln a läuft, x = 8
Faden A läuft, x = 9
Thread B läuft, x = 0
Thread B läuft, x = 1
Thread B läuft, x = 2
Thread B läuft, x = 3
Thread B läuft, x = 4
Thread B läuft, x = 5
Thread B läuft, x = 6
Thread B läuft, x = 7
Thread B läuft, x = 8
Thread B läuft, x = 9
Thread C läuft, x = 0
Thread C läuft, x = 1
Thread C läuft, x = 2
Thread C läuft, x = 3
Thread C läuft, x = 4
Thread C läuft, x = 5
Thread C läuft, x = 6
Thread C läuft, x = 7
Thread C läuft, x = 8
Thread C läuft, x = 9
Wir haben festgestellt, dass die obigen Vorgänge nicht wirklich mit Multi-Threading beginnen, da die Ausführung mehrerer Threads abwechselnd ausgeführt werden muss und zu diesem Zeitpunkt nacheinander ausgeführt wird und der Code jedes Objekts nach der Ausführung des Codes jedes Objekts weiter nach unten ausgeführt wird.
Wenn Sie wirklich Multi-Threading in einem Programm beginnen möchten, müssen Sie sich auf eine Methode der Thread-Klasse verlassen: Public void start (), was bedeutet, dass Sie wirklich mit Multi-Threading beginnen. Nach dem Aufrufen dieser Methode wird die Run () -Methode indirekt aufgerufen:
public class testDemo {public static void main (String [] args) {mythead mt1 = new MyThread ("Thread a"); Mythead mt2 = new MyThread ("Thread B"); Mythead mt3 = new MyThread ("Thread C"); mt1.start (); mt2.Start (); mt3.Start (); }}Auslaufergebnisse:
Thread C läuft, x = 0
Faden A läuft, x = 0
Thread B läuft, x = 0
Fädeln a läuft, x = 1
Thread C läuft, x = 1
Fädeln a läuft, x = 2
Thread B läuft, x = 1
Fädeln a läuft, x = 3
Fädeln a läuft, x = 4
Fädeln a läuft, x = 5
Thread C läuft, x = 2
Thread C läuft, x = 3
Thread C läuft, x = 4
Thread C läuft, x = 5
Thread C läuft, x = 6
Thread C läuft, x = 7
Thread C läuft, x = 8
Thread C läuft, x = 9
Fädeln a läuft, x = 6
Fädeln a läuft, x = 7
Fädeln a läuft, x = 8
Faden A läuft, x = 9
Thread B läuft, x = 2
Thread B läuft, x = 3
Thread B läuft, x = 4
Thread B läuft, x = 5
Thread B läuft, x = 6
Thread B läuft, x = 7
Thread B läuft, x = 8
Thread B läuft, x = 9
Zu diesem Zeitpunkt können Sie feststellen, dass mehrere Threads abwechselnd miteinander ausgeführt werden, die Ergebnisse jeder Ausführung sind jedoch unterschiedlich. Über den obigen Code können wir eine Schlussfolgerung ziehen: Wenn Sie einen Thread starten möchten, müssen Sie sich auf die Ausführung der Thread -Klasse stützen () der Thread -Klasse. Nach dem Start des Threads wird die Run () -Methode standardmäßig aufgerufen.
Nach dem Aufrufen der Start () -Methode passierte eine Reihe komplizierter Dinge:
(1) Starten Sie einen neuen Ausführungs -Thread (mit einem neuen Anrufstapel);
(2) Der Thread wird vom neuen Staat in den Laufstaat übertragen;
(3) Wenn der Thread die Möglichkeit zum Ausführen erhält, wird die Methode Target run () ausgeführt.
Hinweis: Für Java hat die Run () -Methode nichts Besonderes. Wie die main () -Methode bedeutet dies nur, dass der neue Thread den Methodennamen (und die Signatur) des Anrufs kennt. Daher ist es legal, die Run -Methode auf Runnable oder Thread aufzurufen, startet jedoch keinen neuen Thread.
Erläuterung: Warum müssen Sie Start () anrufen, anstatt direkt run () aufzurufen, wenn ein Thread startet?
Wir haben festgestellt, dass nach dem Aufrufen von Start () die überragende Methode überredet () (). Warum also nicht die Run () -Methode direkt aufrufen? Um dieses Problem zu erklären, öffnen Sie den Quellcode der Thread -Klasse und beobachten Sie die Definition der Start () -Methode:
public synchronisierte void start () {if (threadStatus! = 0) werfen neue illaugerTheadStateException (); Group.Add (dies); Boolean begann = falsch; versuche {start0 (); gestartet = wahr; } endlich {try {if (! gestartet) {Group.ThreadStartFailed (this); }} catch (Throwable Ignore) {}}} Private native void start0 ();Öffnen Sie den Quellcode dieser Methode und Sie werden zunächst feststellen, dass die Methode eine Ausnahme von "IllaugerTheadStateException" ausgelegt hat. Im Allgemeinen sollte diese Ausnahme mit einem Versuch mit Throw verwendet, wenn eine Methode verwendet wird, um ein Ausnahmebobjekt zu werfen, mit Versuch oder mit Würfen auf die Methodenerklärung geworfen werden, aber es gibt nichts in diesem Bereich. Warum? Weil diese Ausnahmeklasse zu einer Unterklasse der Laufzeitausnahme (RunTimeException) gehört:
java.lang.object
|- Java.lang.Throwable
|- Java.lang.Exception
|- java.lang.runtimeexception
|- Java.lang.ILLEGALArGumentException
|- Java.lang.ILLEGALTHEADSTATEException
Diese Ausnahme wird ausgelöst, wenn ein Thread -Objekt wiederholt gestartet wird, dh ein Thread -Objekt kann nur einmal gestartet werden.
Eine der kritischsten Teile der Start () -Methode ist die Start0 () -Methode, und diese Methode verwendet eine native Keyword -Definition.
Das native Schlüsselwort bezieht sich auf die Java -native Schnittstelle, dh Java wird verwendet, um die Funktionsfunktionen des nativen Betriebssystems aufzurufen, um einige spezielle Vorgänge auszuführen. Eine solche Codeentwicklung ist in Java fast selten zu beobachten, da das größte Merkmal von Java die Portabilität ist. Wenn ein Programm nur für ein festes Betriebssystem verwendet werden kann, geht die Portabilität vollständig verloren, sodass dieser Vorgang im Allgemeinen nicht verwendet wird.
Die Implementierung von Multithreading muss die Unterstützung des Betriebssystems erfordern. Dann ist die obige Start0 () -Methode der abstrakten Methode ohne Methodenkörper sehr ähnlich. Diese Methode -Körperschaft wird an die JVM übergeben, dh das JVM in Windows kann die A -Methode zum Implementieren von Start0 () verwenden, während der JVM in Linux die B -Methode zum Implementieren von Start0 () verwenden kann. Wenn sie jedoch aufrufen, kümmert sie sich jedoch nicht um die spezifische Methode zur Implementierung der Start0 () -Methode.
Daher erfordert bei der Verwendung der Start () -Methode mit Multi-Thread-Operationen die Funktionsaufrufe von Betriebssystemen mit Multi-Thread-Operationen.
2. Implementieren Sie die Runnable-Schnittstelle, um Multi-Threading zu implementieren
Die Verwendung der Thread-Klasse kann tatsächlich die Multi-Threading-Implementierung erleichtern, aber der größte Nachteil dieser Methode ist das Problem der einzelnen Vererbung. Zu diesem Zweck kann die Runnable-Schnittstelle auch in Java verwendet werden, um Multi-Threading zu implementieren. Die Definition dieser Schnittstelle lautet wie folgt:
öffentliche Schnittstelle Runnable {public void run ();}Implementieren Sie Multi-Threading über die Runnable-Schnittstelle:
Klasse MyThread implementiert runnable {// Der Hauptklassen der Hauptklasse des Threads privat String; public myThread (String title) {this.title = title; } @Override public void run () {// Die Hauptmethode des Threads für (int x = 0; x <10; x ++) {System.out.println (this.title + "run, x =" + x); }}}Dies unterscheidet sich nicht wesentlich von der vorherigen Art der Erben von Thread -Klassen, aber ein Vorteil ist, dass es die Einschränkung der einzigen Vererbung vermeidet.
Aber das Problem ist hier. Wie bereits erwähnt, müssen Sie sich auf die Start () -Methode der Thread-Klasse verlassen, wenn Sie mit einem Multi-Threading beginnen möchten. Wenn Sie die Thread -Klasse erben, können Sie diese Methode direkt erben und verwenden. Aber jetzt implementieren Sie die ablaufbare Schnittstelle. Ohne diese Methode können Sie es erben. Was solltest du tun?
Um dieses Problem zu lösen, müssen Sie sich noch auf die Thread -Klasse verlassen, um es zu vervollständigen. Ein Konstruktor wird in der Thread -Klasse definiert, um das Runnable -Schnittstellenobjekt zu empfangen:
öffentlicher Thread (Runnable -Ziel);
Starten Sie Multithreading mithilfe der Thread -Klasse:
public class testDemo {public static void main (String [] args) löst eine Ausnahme aus {mythead mt1 = new MyThread ("Thread a"); Mythead mt2 = new MyThread ("Thread B"); Mythead mt3 = new MyThread ("Thread C"); neuer Thread (Mt1) .Start (); neuer Thread (MT2) .Start (); neuer Thread (mt3) .Start (); }}Auslaufergebnisse:
Faden A läuft, x = 0
Thread B läuft, x = 0
Thread B läuft, x = 1
Thread C läuft, x = 0
Thread B läuft, x = 2
Fädeln a läuft, x = 1
Thread B läuft, x = 3
Thread C läuft, x = 1
Thread C läuft, x = 2
Thread B läuft, x = 4
Thread B läuft, x = 5
Fädeln a läuft, x = 2
Fädeln a läuft, x = 3
Fädeln a läuft, x = 4
Fädeln a läuft, x = 5
Fädeln a läuft, x = 6
Fädeln a läuft, x = 7
Fädeln a läuft, x = 8
Faden A läuft, x = 9
Thread B läuft, x = 6
Thread B läuft, x = 7
Thread B läuft, x = 8
Thread B läuft, x = 9
Thread C läuft, x = 3
Thread C läuft, x = 4
Thread C läuft, x = 5
Thread C läuft, x = 6
Thread C läuft, x = 7
Thread C läuft, x = 8
Thread C läuft, x = 9
Zu diesem Zeitpunkt wird nicht nur Multi-Thread-Startup erreicht, sondern auch keine einzelnen Vererbungsbeschränkungen.
A
Multithreading unter Verwendung der Runnable -Schnittstelle kann die Einschränkung der einzelnen Vererbung vermeiden. Es gibt jedoch ein Problem, dass die Run () -Methode in der Runnable -Schnittstelle das Betriebsergebnis nicht zurückgeben kann. Um dieses Problem zu lösen, wird eine neue Schnittstelle bereitgestellt: die Callable -Schnittstelle (java.util.concurrent.callable).
öffentliche Schnittstelle Callable <V> {public v call () löst Ausnahme aus;}Nach der Ausführung der Call () -Methode in der Callable -Schnittstelle wird ein Ergebnis zurückgegeben. Der zurückgegebene Ergebnistyp wird durch die Generika auf der Callable -Schnittstelle bestimmt.
Der spezifische Betrieb der Implementierung der Callable-Schnittstelle zur Implementierung von Multi-Threading ist:
Erstellen Sie eine Implementierungsklasse der Callable -Schnittstelle und implementieren Sie die CLALL () -Methode. Verwenden Sie dann die Futuretask -Klasse, um das Objekt der Callable -Implementierungsklasse zu wickeln und dieses Futuretask -Objekt als Ziel des Thread -Objekts, um einen Thread zu erstellen.
Definieren Sie eine Fadenkörperklasse:
import java.util.concurrent @Override public String call () löst Ausnahme aus {für (int i = 0; i <20; i ++) {if (this.ticket> 0) {System.out.println ("Tickets verkaufen, die verbleibende Anzahl von Stimmen ist"+this.ticket -); }} return "Tickets wurden ausverkauft"; }}Die Thread -Klasse unterstützt die Callable -Schnittstelle nicht direkt. Nach JDK1.5 wird eine Klasse zur Verfügung gestellt:
java.util.concurrent.futuretask <v>
Diese Klasse ist hauptsächlich für den Betrieb des Callable Interface -Objekts verantwortlich. Seine Definitionsstruktur ist wie folgt:
öffentliche Klasse Futuretask <V>
erweitert das Objekt
implementiert runnableFurure <v>
Die RunnableFurure -Schnittstelle hat die folgende Definition:
öffentliche Schnittstelle RunnableFurure <V>
erweitert runnable, Future <V>
Der folgende Konstruktor ist in der Futuretask -Klasse definiert:
öffentliches Futuretask (Callable <V> Callable)
Jetzt können Sie endlich das Callable Interface -Objekt über die Futuretask -Klasse empfangen. Der Zweck des Empfangens besteht darin, das Rückgabeergebnis der CALL () -Methode zu erhalten.
Aus der obigen Analyse können wir finden:
Die Futuretask -Klasse kann Callable Interface -Objekte empfangen, und die Futuretask -Klasse implementiert die RunnableFurure -Schnittstelle, die die Runnable -Schnittstelle erbt.
Wir können also Multithreading so beginnen:
public class testDemo {public static void main (String [] args) löst eine Ausnahme aus {mythead mt1 = new MyThread (); Mythead mt2 = new MyThread (); Futuretask <string> Task1 = Neues Futuretask <string> (MT1); // CALL () -Methode zur Rückgabe des Ergebnis Futuretask <string> task2 = new futuretask <string> (mt2); // call () Methode zur Rückgabe des Ergebnisses // Futuretask ist eine Unterklasse des Runnable Interface. Sie können die Thread -Klasse -Konstruktion verwenden, um Taskobjekte neuer Thread (Task1) .Start () zu empfangen. neuer Thread (Task2) .Start (); // Nach Abschluss der Multithread -Ausführung können Sie die Get () -Methode in der übergeordneten Schnittstelle von Futuretask verwenden, um das Ausführungsergebnissystem zu erhalten. System.out.println ("Thread 2 -Rückgabeergebnis:"+task2.get ()); }}Auslaufergebnisse:
Tickets verkaufen, die verbleibende Anzahl von Stimmen beträgt 10
Tickets verkaufen, die verbleibende Anzahl von Stimmen beträgt 10
Tickets verkaufen, die verbleibende Anzahl von Stimmen beträgt 9
Tickets verkaufen, die verbleibende Anzahl von Stimmen beträgt 8
Tickets verkaufen, die verbleibende Anzahl von Stimmen beträgt 7
Tickets verkaufen, die verbleibende Anzahl von Stimmen beträgt 9
Tickets verkaufen, die verbleibende Anzahl von Stimmen beträgt 6
Tickets verkaufen, die verbleibende Anzahl von Stimmen beträgt 8
Tickets verkaufen, die verbleibende Anzahl von Stimmen beträgt 5
Tickets verkaufen, die verbleibende Anzahl von Stimmen beträgt 7
Tickets verkaufen, die verbleibende Anzahl von Stimmen beträgt 4
Tickets verkaufen, die verbleibende Anzahl von Stimmen beträgt 6
Tickets verkaufen, die verbleibende Anzahl von Stimmen beträgt 3
Tickets verkaufen, die verbleibende Anzahl von Stimmen beträgt 5
Tickets verkaufen, die verbleibende Anzahl von Stimmen beträgt 2
Tickets verkaufen, die verbleibende Anzahl von Stimmen beträgt 4
Tickets verkaufen, die verbleibende Anzahl von Stimmen beträgt 1
Tickets verkaufen, die verbleibende Anzahl von Stimmen beträgt 3
Tickets verkaufen, die verbleibende Anzahl von Stimmen beträgt 2
Tickets verkaufen, die verbleibende Anzahl von Stimmen beträgt 1
Das Rückgabeergebnis von Thread 1: Das Ticket wurde ausverkauft. Das Rückgabeergebnis von Thread 2: Das Ticket wurde ausverkauft.
Zusammenfassung:
Das obige erklärt drei Möglichkeiten zur Implementierung von Multithreading. Für das Thread -Start werden sie alle als Start () -Methode des Thread -Objekts bezeichnet. Es ist wichtig zu beachten, dass die Start () -Methode nicht zweimal auf demselben Thread -Objekt aufgerufen werden kann.