Inhaltsverzeichnis(?)[-]
Man erweitert die Javalangthread -Klasse zwei implementiert die Javalangrunnable -Schnittstelle drei Unterschiede zwischen Thread und Runnable Four Thread -Status -Übergang Fünf Thread -Planung Sechs gemeinsame Funktionen, in denen erläutert wird
In diesem Artikel geht es hauptsächlich um die Verwendungsmethoden des Multi-Threading in Java, Threadsynchronisation, Thread-Datenübertragung, Threadstatus und entsprechender Thread-Funktionsverbrauch und Übersicht.
Lassen Sie uns zunächst über den Unterschied zwischen einem Prozess und einem Thread sprechen:
Prozess: Jeder Prozess verfügt über einen unabhängigen Code- und Datenraum (Prozesskontext), und das Umschalten zwischen den Prozessen hat einen großen Overhead. Ein Prozess enthält 1-N-Threads.
Thread: Der gleiche Typ von Threads teilen Code und Datenraum. Jeder Thread verfügt über einen unabhängigen Running -Stack und einen Programmzähler (PC), und der Überkopfschalter der Fadenschaltung ist gering.
Wie bei einem Prozess sind die Themen in fünf Phasen unterteilt: Erstellung, Bereit, Laufen, Blockieren und Beenden.
Multi-Process bedeutet, dass das Betriebssystem gleichzeitig mehrere Aufgaben (Programme) ausführen kann.
Multithreading bezieht sich auf mehrere sequentielle Streams, die im selben Programm ausgeführt werden.
In Java gibt es zwei Möglichkeiten, Multi-Threading zu implementieren. Einer besteht darin, die Thread -Klasse fortzusetzen, und das andere ist die Implementierung der rannbaren Schnittstelle.
1. Erweitern Sie die Klasse von Java.lang.Thread
Paket com.multithread.learning;/***@functon multithreading Learning*@Autor Lin Bingwen*@time 2015.3.9*/class Thread1 erweitert Thread {private String -Name; public thread1 (String name) {this.name = name; } public void run () {für (int i = 0; i <5; i ++) {System.out.println (Name + "run:" + i); try {sleep ((int) math.random () * 10); } catch (interruptedException e) {e.printstacktrace (); }}}} public class main {public static void main (String [] args) {Thread1 mth1 = neuer Thread1 ("a"); Thread1 Mth2 = neuer Thread1 ("B"); mth1.Start (); mth2.Start (); }} Ausgabe:
Ein Lauf: 0
B Run: 0
Ein Lauf: 1
Ein Lauf: 2
Ein Lauf: 3
Ein Lauf: 4
B Run: 1
B Run: 2
B Run: 3
B Run: 4
Führen Sie es noch einmal aus:
Ein Lauf: 0
B Run: 0
B Run: 1
B Run: 2
B Run: 3
B Run: 4
Ein Lauf: 1
Ein Lauf: 2
Ein Lauf: 3
Ein Lauf: 4
veranschaulichen:
Wenn das Programm startet und ausführt, startet die Java Virtual Machine einen Prozess, und das Hauptfaden -Main wird erstellt, wenn der Main () aufgerufen wird. Mit der Startmethode der beiden Objekte von Misay werden auch die beiden anderen Fäden gestartet, so dass die gesamte Anwendung unter mehreren Threads ausgeführt wird.
HINWEIS: Die Methode start () wird aufgerufen, um Multi-Thread-Code nicht sofort auszuführen, sondern lässt den Thread zu einem Runnable-Status werden. Wenn es ausgeführt wird, wird das Betriebssystem bestimmt.
Aus den Ergebnissen des ausgeführten Programms können wir feststellen, dass Multi-Thread-Programme außerordentlich ausgeführt werden. Daher muss nur Code, der außerhalb der Reihenfolge ausgeführt wird, als Multi-Threaded ausgelegt werden.
Der Zweck des Aufrufs der Thread.Sleep () besteht darin, zu verhindern, dass der aktuelle Thread die durch den Prozess erhaltenen CPU -Ressourcen allein besetzt, um eine bestimmte Zeit für andere Threads auszuführen.
Tatsächlich ist die Ausführungsreihenfolge aller Multi-Thread-Code ungewiss, und die Ergebnisse jeder Ausführung sind zufällig.
Wenn die Startmethode jedoch wiederholt aufgerufen wird, tritt eine Java.lang.IilleGalthreadStateException auf.
Thread1 mth1 = neuer Thread1 ("a"); Thread1 Mth2 = Mth1; mth1.Start (); mth2.Start (); Ausgabe:
Ausnahme in Thread "Haupt" java.lang.IillegeldeadstateException
bei java.lang.thread.start (unbekannte Quelle)
bei com.multithread.learning.main.main (main.java:31)
Ein Lauf: 0
Ein Lauf: 1
Ein Lauf: 2
Ein Lauf: 3
Ein Lauf: 4
2. Implementieren Sie die Schnittstelle java.lang.runnable
/***@functon multithreading lern*@autor lin bingwen*@time 2015.3.9*/package com.multitHhread.runnable; Klasse Thread2 implements Runnable {private String -Name; public thread2 (String name) {this.name = name; } @Override public void run () {für (int i = 0; i <5; i ++) {System.out.println (Name + "run:" + i); try {thread.sleep ((int) math.random () * 10); } catch (interruptedException e) {e.printstacktrace (); }}}} public class main {public static void main (String [] args) {neuer Thread (neuer Thread2 ("c")). start (); neuer Thread (neuer Thread2 ("D")). start (); }} Ausgabe:
C Run: 0
D Run: 0
D Run: 1
C Run: 1
D Run: 2
C Run: 2
D Run: 3
C Run: 3
D Run: 4
C Run: 4
veranschaulichen:
Die Thread2-Klasse implementiert die Runnable-Schnittstelle, wodurch die Klasse die Eigenschaften einer Multi-Threaden-Klasse hat. Die Run () -Methode ist eine Konvention für Multithread -Programme. Der gesamte Multi-Thread-Code befindet sich in der Run-Methode. Die Thread -Klasse ist tatsächlich eine Klasse, die die runnable Schnittstelle implementiert.
Beim Starten von Multi-Threading müssen Sie das Objekt zuerst über den Thread-Class-Konstruktor-Thread (Runnable-Ziel) konstruieren und dann die Start () -Methode des Thread-Objekts aufrufen, um den Multi-Thread-Code auszuführen.
Tatsächlich wird alle Multithread -Code durch Ausführen von Thread's start () -Methode ausgeführt. Unabhängig davon, ob die Thread-Klasse erweitert oder die Runnable-Schnittstelle implementiert wird, um Multi-Threading zu implementieren, oder letztendlich Threads durch die Thread-Objekt-API steuern, ist es die Grundlage für die Programmierung von Multi-Thread-Programmen.
3.. Der Unterschied zwischen Thread und Runnable
Wenn eine Klasse Thread erbt, ist er nicht für die Ressourcenfreigabe geeignet. Wenn jedoch die ablaufbare Schnittstelle implementiert ist, ist es einfach, die Ressourcenfreigabe zu implementieren.
Paket com.multithread.learning;/***@functon Multi-thread Learning, Erben-Thread, Ressourcen können nicht gemeinsam genutzt werden privater Zeichenfolge Name; public thread1 (String name) {this.name = name; } public void run () {für (int i = 0; i <5; i ++) {System.out.println (Name + "run count =" + count--); try {sleep ((int) math.random () * 10); } catch (interruptedException e) {e.printstacktrace (); }}}} public class main {public static void main (String [] args) {Thread1 mth1 = neuer Thread1 ("a"); Thread1 Mth2 = neuer Thread1 ("B"); mth1.Start (); mth2.Start (); }} Ausgabe:
B Run Count = 5
Eine Laufzählung = 5
B Run Count = 4
B Run Count = 3
B Run Count = 2
B Run Count = 1
Eine Laufzahl = 4
Eine Laufzahl = 3
Eine Laufzählung = 2
Eine Laufzählung = 1
Aus den oben genannten Punkten können wir feststellen, dass die Zählungen zwischen verschiedenen Threads unterschiedlich sind, was ein großes Problem für das Ticketverkaufssystem hat. Natürlich kann hier eine Synchronisation verwendet werden. Verwenden wir Runnable, um es hier zu tun
/***@functon Multi-threading-Lernerben, die runnable, Ressourcen können geteilt werden. @Override public void run () {für (int i = 0; i <5; i ++) {System.out.println (Thread.currentThread (). GetName () + "run count =" + count--); try {thread.sleep ((int) math.random () * 10); } catch (interruptedException e) {e.printstacktrace (); }}}}} public class main {public static void main (string [] args) {thread2 my = new Thread2 (); neuer Thread (mein "C"). start (); // derselbe MT, aber es ist im Thread nicht möglich. Wenn Sie das Objekt MT instanziieren, wird eine Ausnahme neuer Thread (My, "D"). Start (); neuer Thread (mein "E"). starten (); }} Ausgabe:
C Lauf zählen = 15
D Run Count = 14
E run count = 13
D Run Count = 12
D Run Count = 10
D Run Count = 9
D Run Count = 8
C Lauf zählen = 11
E run count = 12
C Lauf zählen = 7
E Run Count = 6
C Lauf zählen = 5
E run count = 4
C Lauf zählen = 3
E run count = 2
Hier sollten wir beachten, dass jeder Thread das gleiche Instanziierungsobjekt verwendet. Wenn es nicht dasselbe ist, ist der Effekt der gleiche wie oben!
Zusammenfassen:
Die Vorteile der Implementierung der Runnable -Schnittstelle über die Erben der Thread -Klasse:
1): Geeignet für mehrere Threads mit demselben Programmcode, um dieselbe Ressource zu verarbeiten
2): Kann die Einschränkung des einzelnen Vererbung in Java vermeiden
3): Erhöhen Sie die Robustheit des Programms, der Code kann durch mehrere Threads gemeinsam genutzt werden und der Code und die Daten sind unabhängig
Lassen Sie mich Sie erinnern: Die Hauptmethode ist tatsächlich ein Thread. In Java werden die Fäden gleichzeitig gestartet. Was wann und welcher zuerst ausgeführt wird, hängt vollständig davon ab, wer die CPU -Ressourcen zuerst erhält.
In Java werden jedes Mal mindestens 2 Threads gestartet, wenn das Programm ausgeführt wird. Einer ist der Hauptfaden und der andere ist der Garbage Collection -Faden. Denn wenn eine Klasse mit Java -Befehlen ausgeführt wird, wird tatsächlich ein JVM gestartet, und jedes JVM -Praktikum startet einen Prozess im Betriebssystem.
4. Übergang von Thread State
1. New State (neu): Es wird ein neues Thread -Objekt erstellt.
2. Ready State (Runnable): Nachdem das Thread -Objekt erstellt wurde, rufen andere Threads die Start () -Methode des Objekts auf. Der Thread in diesem Zustand befindet sich im Runnable -Thread -Pool und wird laufbar. Es wartet darauf, die CPU -Nutzungsrechte zu erhalten.
3. Auslaufstatus: Der Thread im Ready State erwirbt die CPU und führt den Programmcode aus.
4. Blockierter Zustand: Blockierter Zustand bedeutet, dass der Thread die CPU -Nutzungsrechte aus irgendeinem Grund aufgibt und vorübergehend nicht mehr läuft. Erst wenn der Thread in den bereiten Zustand eingeht, hat er die Chance, in den Laufstatus zu gehen. Es gibt drei Arten von Blockaden:
.
(2) Synchronen Blockierung: Wenn der laufende Thread die Synchronisationsschloss des Objekts erfasst, stellt das JVM den Faden in den Sperrpool.
(Iii), andere Blockierung: Wenn ein laufender Thread die Methode Sleep () oder join () ausführt oder eine E/A -Anforderung ausgibt, setzt der JVM den Thread in einen Blockierungszustand. 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 Run () -Methode aufgrund einer Ausnahme beendet oder beendet, und der Thread beendet seinen Lebenszyklus.
5. Thread -Planung
Threadplanung
1. Priorität einstellen: Java -Threads haben Priorität, und Threads mit hoher Priorität erhalten mehr Möglichkeiten zum Laufen.
Die Priorität von Java -Threads wird durch Ganzzahlen mit einem Wertebereich von 1 ~ 10 dargestellt. Die Thread -Klasse hat die folgenden drei statischen Konstanten:
statische int max_priority
Die höchste Priorität, die ein Thread haben kann, ist 10.
statische int min_priority
Die niedrigste Priorität, die ein Thread haben kann, ist 1.
statische int norm_priority
Die dem Thread zugewiesene Standardpriorität beträgt 5.
Die Methoden SetPriority () und GetPriority () der Thread -Klasse werden verwendet, um die Priorität des Threads zu setzen und zu erhalten.
Jeder Thread hat eine Standardpriorität. Die Standardpriorität des Haupt -Threads ist thread.norm_priority.
Die Priorität von Threads wird vererbt. Wenn beispielsweise Thread B in Thread A erstellt wird, hat B die gleiche Priorität wie A.
Das JVM bietet 10 Thread -Prioritäten, kartiert jedoch nicht gut mit gemeinsamen Betriebssystemen. Wenn Sie möchten, dass das Programm auf jedes Betriebssystem portiert wird, sollten Sie die Thread -Klasse nur mit den folgenden drei statischen Konstanten als Priorität verwenden, um sicherzustellen, dass die gleiche Priorität dieselbe Zeitplanungsmethode annimmt.
2. Fadenschlaf: Faden.S -Sleep (Long Millis) Methode, um den Faden zu einem Blockierungszustand zu machen. Der Millis -Parameter setzt die Schlafzeit in Millisekunden. Wenn der Schlaf vorbei ist, ist er läuft. Die Sleep () -Plattform hat eine gute Portabilität.
3. Thread Warted: Die Wait () -Methode in der Objektklasse bewirkt, dass der aktuelle Thread wartet, bis andere Threads die melden () -Methode des Objekts oder die Weckmethode von NotifyAll () aufrufen. Diese beiden Weckmethoden sind auch Methoden in der Objektklasse, und ihr Verhalten entspricht dem Aufrufen von Warten (0).
4. Thread -Konzessionen: Thread.yield () Methode setzt das aktuell ausführende Thread -Objekt aus und gibt die Ausführungsmöglichkeit, Threads mit derselben oder höheren Priorität zu tätigen.
5. Thread Join: Join () -Methode, wartet auf die Beendigung anderer Threads. Wenn der aktuelle Thread die monjoy () -Methode eines anderen Threads im aktuellen Thread aufgerufen hat, geht der aktuelle Thread zu einem Blockierungsstatus, bis der andere Prozess ausgeführt wird, und der aktuelle Thread geht von der Blockierung zum Ready -Status.
6. Thread Wake-up: Die melden () -Methode in der Objektklasse weckt einen einzelnen Thread auf diesem Objektmonitor. Wenn alle Threads auf dieses Objekt warten, wird einer der Threads ausgewählt. Die Wahl ist willkürlich und tritt bei einer Entscheidung über die Umsetzung auf. Der Thread wartet auf dem Monitor des Objekts, indem er eine der Warteverfahren aufruft. Der Waken -Thread kann erst ausgeführt werden, wenn der aktuelle Thread die Sperre für dieses Objekt verlässt. Der Waken -Thread konkurriert mit allen anderen Threads, die aktiv auf das Objekt synchronisiert sind. Zum Beispiel hat der Waken -Thread keine zuverlässigen Berechtigungen oder Nachteile, wenn es darum geht, der nächste Thread zu sein, der dieses Objekt sperrt. Eine ähnliche Methode hat auch eine Notifyall (), die alle Threads aufwacht, die auf diesen Objektmonitor warten.
HINWEIS: Die beiden Methoden suspend () und resume () in Thread wurden in JDK1.5 abgeschafft und werden nicht erneut eingeführt. Weil es eine Tendenz zum Deadlock gibt.
6. Beschreibung der gemeinsamen Funktionen
① Sleep (Long Millis): Lassen Sie den aktuell ausführenden Threadschlaf innerhalb der angegebenen Anzahl von Millisekunden (Suspend Execution)
②Join (): Bezieht sich auf das Warten auf den T -Thread.
Wie man es benutzt.
Join ist eine Methode der Thread -Klasse. Es wird direkt nach dem Start des Threads aufgerufen. Das heißt, die Funktion von join () lautet: "Warten Sie, bis der Thread endet". Was hier verstanden werden muss, ist, dass sich der Thread auf den Hauptfaden bezieht, der darauf wartet, dass der Kinderfaden endet. Das heißt, der Code nach dem untergeordneten Thread ruft die join () -Methode auf und kann nur ausgeführt werden, bis der untergeordnete Thread fertig ist.
Thread t = neuer tead (); t.start (); T.Join ();
Warum verwenden Sie die join () -Methode
In vielen Fällen erzeugt und startet der Hauptfaden den Kinderfaden. Wenn im Kinderfaden eine große Anzahl von zeitaufwändigen Operationen erforderlich ist, endet der Hauptfaden häufig vor dem Kinderfaden. Wenn der Haupt -Thread jedoch das Verarbeitungsergebnis des untergeordneten Threads nach der Verarbeitung anderer Transaktionen verwenden muss, muss der Haupt -Thread warten, bis der untergeordnete Thread vor dem Ende die Ausführung abschließt. Zu diesem Zeitpunkt muss die join () -Methode verwendet werden.
Kein Join. /** *@functon multithreading Learning, Join *@Autor Lin Bingwen *@time 2015.3.9 */package com.multitHhread.Join; public Thread1 (Zeichenfolge Name) {Super (Name); this.name = name; } public void run () {System.out.println (Thread.currentThread (). getName () + "Thread startet!"); für (int i = 0; i <5; i ++) {System.out.println ("subthread"+name+"run:"+i); try {sleep ((int) math.random () * 10); } catch (interruptedException e) {e.printstacktrace (); }} System.out.println (Thread.currentThread (). GetName () + "Thread Run endet!"); }} public class main {public static void main (string [] args) {System.out.println (Thread.currentThread (). getName ()+"Haupt -Thread Run Start!"); Thread1 mth1 = neuer Thread1 ("a"); Thread1 Mth2 = neuer Thread1 ("B"); mth1.Start (); mth2.Start (); System.out.println (Thread.currentThread (). GetName ()+ "Hauptfadenlauf endet!"); }} Ausgangsergebnis:
Hauptfaden beginnt zu laufen!
Hauptfaden endet läuft!
B Der Thread -Lauf beginnt!
Kinder Thread B läuft: 0
Ein Thread -Lauf beginnt!
Kinderfaden a läuft: 0
Kinder Thread B läuft: 1
Kinderfaden a läuft: 1
Kinderfaden a läuft: 2
Kinderfaden a läuft: 3
Kinderfaden a läuft: 4
Ein Thread -Lauf endet!
Kinder Thread B läuft: 2
Kinder Thread B läuft: 3
Kinder Thread B läuft: 4
B Der Thread läuft vorbei!
Stellte fest, dass der Hauptfaden früher als der untergeordnete Thread endete
Verbinden
public class main {public static void main (String [] args) {System.out.println (Thread.CurrentThread (). getName ()+"Hauptsthread -Start!"); Thread1 mth1 = neuer Thread1 ("a"); Thread1 Mth2 = neuer Thread1 ("B"); mth1.Start (); mth2.Start (); try {mth1.join (); } catch (interruptedException e) {e.printstacktrace (); } try {mth2.join (); } catch (interruptedException e) {e.printstacktrace (); } System.out.println (Thread.currentThread (). GetName ()+ "Der Haupt -Thread -Lauf endet!"); }} Auslaufergebnisse:
Hauptfaden beginnt zu laufen!
Ein Thread -Lauf beginnt!
Kinderfaden a läuft: 0
B Der Thread -Lauf beginnt!
Kinder Thread B läuft: 0
Kinderfaden a läuft: 1
Kinder Thread B läuft: 1
Kinderfaden a läuft: 2
Kinder Thread B läuft: 2
Kinderfaden a läuft: 3
Kinder Thread B läuft: 3
Kinderfaden a läuft: 4
Kinder Thread B läuft: 4
Ein Thread -Lauf endet!
Der Hauptfaden wird auf jeden Fall warten, bis die Kinderfäden fertig sind, bevor er endet.
③yield (): pausiert das aktuell ausführende Thread -Objekt und führt andere Threads aus.
Die Funktion der Thread.yield () -Methode lautet: Unterhalten Sie das derzeit ausgeführte Thread -Objekt und führen Sie andere Threads aus.
Was Rendite () tun sollte, ist, den aktuellen Lauf -Thread wieder in den Runnable -Status zu bringen, damit andere Threads mit der gleichen Priorität eine Run -Gelegenheit erhalten. Daher besteht der Zweck der Verwendung von Rendite () darin, Threads derselben Priorität angemessen zu ermöglichen. In der Realität kann Rendite () jedoch nicht garantiert werden, dass er den Zweck der Konzession erreicht hat, da der Konzessions -Thread vom Thread -Scheduler erneut ausgewählt werden kann.
Schlussfolgerung: Rendite () führt niemals dazu, dass der Faden zum Warten-/Schlaf-/Blockierungszustand geht. In den meisten Fällen wird Rendite () dazu führen, dass der Thread vom Laufstaat zum Laufen verläuft, aber möglicherweise nicht funktioniert. Sie können das Bild oben sehen.
/** *@functon multithreading Learning Rendite *@Autor Lin Bingwen *@time 2015.3.9 */package com.multitHhread.yield; Klasse Thradyield erweitert Thread {public Thradyield (String -Name) {Super (Name); } @Override public void run () {für (int i = 1; i <= 50; i ++) {System.out.println ("" + this.getName () + "------" + i); // Wenn ich 30 ist, gibt der Thread die CPU -Zeit auf und lässt andere oder seine eigenen Threads ausführen (dh, wer sie zuerst greift), wenn (i == 30) {this.yield (); }}}} public class main {public static void main (String [] args) {Thradyield yt1 = new Thradyield ("Zhang san"); Threadyield yt2 = new Thradyield ("li si"); yt1.Start (); yt2.Start (); }} Auslaufergebnisse:
Der erste Fall: Li Si (Thread) erhält die CPU -Auszeit, wenn sie auf 30 ausgeführt wird. Zu diesem Zeitpunkt greift Zhang San (Thread) die CPU -Zeit und führt sie aus.
Die zweite Situation: Wenn Li Si (Thread) auf 30 ausführt, wird die CPU -Zeit aufgegeben. Zu diesem Zeitpunkt greift Li Si (Thread) die CPU -Zeit und führt sie aus.
Der Unterschied zwischen Schlaf () und Ertrag ()
Der Unterschied zwischen Schlaf () und Ertrag ()): Sleep () bewirkt, dass der aktuelle Thread in einen stagnierenden Zustand eintritt, sodass der Thread Sleep () in der angegebenen Zeit nicht ausgeführt wird; Rendite () bewirkt nur, dass der aktuelle Thread zum ausführbaren Status zurückkehrt, sodass der ausführende Thread Rendite () unmittelbar nach Eingabe des ausführbaren Status ausgeführt wird.
Die Schlafmethode bewirkt, dass der derzeit laufende Faden für einen bestimmten Zeitraum schläft und in einen unüberlegten Zustand kommt. Die Länge dieser Zeit wird vom Programm festgelegt. Die Ertragsmethode ermöglicht es dem aktuellen Thread, das CPU -Besitz aufzugeben, aber die Zeit der Übertragung ist unangemessen. Tatsächlich entspricht die Methode Relief () der folgenden Operation: Überprüfen Sie zunächst, ob Threads mit der gleichen Priorität derzeit im selben laufbaren Zustand vorhanden sind. Wenn ja, geben Sie den CPU -Besitz an diesem Thread weiter, andernfalls führen Sie den ursprünglichen Thread weiter aus. Die Methode Relief () wird also als "Konzession" bezeichnet, wodurch andere Threads mit der gleichen Priorität die Gelegenheit dazu veranlasst werden können
Darüber hinaus ermöglicht die Schlafmethode mit niedrigeren Prioritätsthreads Run -Möglichkeiten. Wenn jedoch die Rendite () -Methode ausgeführt wird, befindet sich der aktuelle Thread noch in einem laufbaren Zustand. Daher ist es unmöglich, Threads mit niedrigerer Priorität aufzugeben, um die CPU -Besitzer später zu erhalten. Wenn in einem laufenden System der Thread mit höherer Priorität die Schlafmethode nicht aufruft und nicht durch E/A blockiert wird, kann der Thread mit niedrigerer Priorität nur darauf warten, dass alle Threads mit höherer Priorität ausgeführt werden, um die Chance zum Ausführen zu haben.
④SetPriority (): Ändern Sie die Priorität des Threads.
Min_priority = 1
Norm_priority = 5
Max_priority = 10
Verwendung:
Thread4 T1 = neuer Thread4 ("T1");
Thread4 T2 = neuer Thread4 ("T2");
t1.setPriority (thread.max_priority);
t2.setPriority (thread.min_priority);
⑤Interrupt (): Unterbrechen Sie einen Thread. Diese Endmethode ist ziemlich rau. Wenn der T -Thread eine Ressource öffnet und keine Zeit hatte, ihn zu schließen, dh die Run -Methode ist gezwungen, den Thread vor seiner Ausführung zu beenden, wodurch die Ressource nicht schließt.
Der beste Weg, um den Prozess zu beenden, besteht darin, das Beispielprogramm der Sleep () -Funktion zu verwenden. Eine boolesche Variable wird in der Thread -Klasse verwendet, um zu steuern, wenn die Run () -Methode endet. Sobald die Run () -Methode endet, endet der Faden.
⑥wait ()
Obj.wait () und obj.notify () müssen mit synchronisiertem (OBJ) verwendet werden, dh warten und benachrichtigen, die auf der erworbenen OBJ -Sperre operieren. Aus synchronisierter Sicht ist es obj.wait (), und obj.notify muss sich im Synchronisierungsblock (OBJ) {...} befinden. Warten Sie aus funktionaler Perspektive, dass nach dem Erwerb des Fadens die Objektschloss aktiv die Objektschloss freigibt und der Faden schläft. Die Objektschloss kann nicht erhalten werden und die Ausführung wird fortgesetzt, bis ein anderer Thread das Objekt benachrichtigt (), um den Thread aufzuwecken. Der entsprechende Benachrichtigung () ist der Weckbetrieb der Objektschloss. Eine Sache zu beachten ist jedoch, dass nach dem Notify () -Anruf die Objektsperrung nicht sofort freigegeben wird, aber die Ausführung des entsprechenden synchronisierten () {} Anweisungsblocks wird abgeschlossen und die Sperre wird automatisch freigegeben, der JVM wählt zufällig einen Thread aus dem Wait () Objektsperr -Thread, zuordnen Sie die Objektsperrung, weckt den Faden weiter und führt die Ausführung weiter aus. Dies bietet Synchronisation und Weckvorgänge zwischen Threads. Sowohl thread.sleep () als auch Object.wait () kann den aktuellen Thread pausieren und die CPU -Steuerung freigeben. Der Hauptunterschied besteht darin, dass während Object.wait () die CPU freigibt, es die Kontrolle der Objektschloss freigibt.
Es reicht nicht aus, konzeptionell zu verstehen, und es muss in praktischen Beispielen getestet werden, um besser zu verstehen. Das klassischste Beispiel für die Anwendung von Object.wait () und Object.notify () sollte das Problem des Druckens von ABC mit drei Threads sein. Dies ist eine relativ klassische Interviewfrage, und die Fragen sind wie folgt:
Drei Threads festlegen, Faden A druckt ein 10 -fach, Thread B druckt B 10 Mal, Thread C druckt c 10 Mal, Thread C muss gleichzeitig ausgeführt werden und ABC wird zehnmal abwechselnd gedruckt. Dieses Problem kann leicht gelöst werden, indem das Warten () und benachrichtigen () des Objekts verwendet wird. Der Code ist wie folgt:
/** * Wait Useage * @author treamea * @time 2015.3.9 */package com.multithread.wait; öffentliche Klasse mytheadPrinter2 implementiert Runnable {private String -Name; privates Objekt prev; privates Objekt Selbst; private MyTheadPrinter2 (Zeichenfolge Name, Objektprävention, Objekt selbst) {this.name = name; this.prev = prev; this.self = self; } @Override public void run () {int count = 10; while (count> 0) {synchronized (prev) {synchronized (self) {System.out.print (Name); zählen--; self.notify (); } try {prev.wait (); } catch (interruptedException e) {e.printstacktrace (); }}}} public static void main (String [] args) löst eine Ausnahme aus {Object a = new Object (); Objekt b = neues Objekt (); Objekt c = neues Objekt (); MyTheadPrinter2 pa = new MyThreadPrinter2 ("a", c, a); MyThreadPrinter2 PB = New MyThreadPrinter2 ("B", a, b); MyThreadPrinter2 PC = New MyThreadPrinter2 ("C", B, C); neuer Thread (pa) .Start (); Thread.Sleep (100); // stellen Sie sicher, dass Sie einen neuen Thread (pb) ausführen .Start (); Thread.Sleep (100); }} Ausgangsergebnis:
ABCABCABCABCABCABCABCABCABCABCABCABC
Erklären wir zunächst die Gesamtidee. Aus allgemeiner Sicht ist dieses Problem ein synchroner Weckvorgang zwischen drei Threads. Der Hauptzweck besteht darin, drei Threads in Threada-> Threadc-> Threada-Schleife auszuführen. Um die Reihenfolge der Thread-Ausführung zu steuern, muss die Reihenfolge des Aufwachens und des Wartens ermittelt werden, sodass jeder Thread zwei Objektschlösser gleichzeitig enthalten muss, bevor er die Ausführung fortsetzen kann. Eine Objektschloss ist vorhanden, bei der die vom vorherige Thread gehaltene Objektschloss ist. Ein anderer ist das Schloss des Objekts. Die Hauptidee ist, dass Sie, um die Reihenfolge der Ausführung zu steuern, zuerst die vorherrschende Sperre halten müssen, dh der vorherige Thread muss eine eigene Objektsperrung freigeben und dann für eine eigene Objektschloss angewendet werden. Druck, wenn beide beide sind. Rufen Sie dann zuerst self.notify () auf, um eine eigene Objektsperrung zu veröffentlichen, den nächsten Wartepreis zu wecken und dann Prev.wait () aufzurufen, um die Vorgabebuchsperrung freizulassen, den aktuellen Thread zu beenden und darauf zu warten, dass die Schleife erneut erweckt wird. Führen Sie den obigen Code aus und Sie können feststellen, dass drei Threads ABC in einer Schleife drucken, insgesamt 10 Mal. Der Hauptprozess des Programms ist, dass Thread A der erste ist, der das Laufen hält, die Objektschlösser von C und A enthält und dann die Schlösser von A und C freigibt und auf Waking B. Thread B auf die Sperre A wartet, wendet das Schloss B auf, druckt B, dann druckt B, dann löst B, ein Schloss, Wakes C, Thread C wartet auf Schloss B. Problem, aber wenn Sie sorgfältig darüber nachdenken, werden Sie feststellen, dass es ein Problem gibt, das die Anfangsbedingung ist. Die drei Fäden werden in der Reihenfolge von A, B und C nach den vorherigen Gedanken begonnen. A wacht B, B auf C, C auf und weckt dann A. Diese Annahme hängt jedoch von der Reihenfolge der Thread -Planung und -ausführung in der JVM ab.
Der Unterschied zwischen Wartezeit und Schlaf
Häufige Punkte:
1. Sie befinden sich alle in einer Multi-Thread-Umgebung und können die angegebene Anzahl von Millisekunden bei der Programmanruf und Rückgabe blockieren.
2. Sowohl Wait () als auch Sleep () können den Pause Status des Threads durch die Interrupt () -Methode unterbrechen, so dass der Faden sofort eine InterruptedException auswirkt.
Wenn Thread A sofort Thread B beenden will, kann die Interrupt -Methode auf der Thread -Instanz aufgerufen werden, die dem Thread B entspricht. Wenn Thread B in diesem Moment wartet/schlaf/Join, wirft Thread B sofort eine InterruptedException aus und gibt ihn direkt in catch () {} zurück, um den Thread sicher zu beenden.
Es ist zu beachten, dass die InterruptedException vom Faden selbst von innen und nicht durch die Interrupt () -Methode geworfen wird. Wenn Interrupt () in einem Thread aufgerufen wird, wird der Thread, wenn der Thread einen normalen Code ausführt, eine InterruptedException überhaupt nicht. Sobald der Thread jedoch auf Wait ()/Sleep ()/join () eintritt, wird eine InterruptedException sofort geworfen.
Unterschiede:
1. Thread -Klassenmethoden: Sleep (), Rendite () usw.
Objektmethoden: Wait () und notify () usw.
2. Jedes Objekt verfügt über ein Schloss zur Steuerung des synchronen Zugriffs. Das synchronisierte Schlüsselwort kann mit der Schloss des Objekts interagieren, um die Threadsynchronisation zu realisieren.
Die Schlafmethode löst das Schloss nicht frei, während die Warteverfahren das Schloss veröffentlicht, sodass andere Threads synchrone Steuerblöcke oder -methoden verwenden können.
3. Warten Sie, benachrichtigen Sie und benachrichtigen Sie nur in Synchronisationssteuermethoden oder Synchronisationssteuerblöcken, während der Schlaf überall verwendet werden kann.
4. Der Schlaf muss Ausnahmen erfassen, während Sie warten, benachrichtigen und benachrichtigen müssen, müssen keine Ausnahmen fangen. Der größte Unterschied zwischen Schlaf () und Wait () () Methoden sind daher:
Wenn der Schlaf () schläft, halten Sie das Objektschloss und besitzen immer noch das Schloss.
Wenn Wait () schläft, wird das Objektschloss freigegeben.
Wait () und Sleep () können jedoch sowohl den Pause Status des Threads über die Interrupt () -Methode unterbrechen, so dass der Thread sofort eine InterruptedException auswirkt (es wird jedoch nicht empfohlen, diese Methode zu verwenden).
Sleep () Methode
Sleep () bewirkt, dass der aktuelle Thread in einen stagnierenden Zustand eingeht (blockiert den aktuellen Thread), wobei die Verwendung von Cup aufgibt, und es ist der Zweck, zu verhindern, dass der aktuelle Thread die durch den Vorgang erhaltenen CPU -Ressourcen in Ruhe einnimmt, um eine bestimmte Zeit für andere Threads auszuführen;
Sleep () ist eine statische Methode der Thread -Klasse; Daher kann es nicht die Maschinenschloss des Objekts ändern. Wenn Sie also die Sleep () -Methode in einem synchronisierten Block aufrufen, wird das Maschinenschloss des Objekts nicht freigegeben und andere Fäden können nicht auf das Objekt zugreifen (obwohl es das Objektschloss auch dann hält, selbst wenn es schläft).
Nach Ablauf des Schlafes () Schlaf () wird der Thread nicht unbedingt sofort ausgeführt, da andere Threads möglicherweise ausgeführt werden und nicht die Ausführung aufgeben sollen, es sei denn, der Thread hat eine höhere Priorität.
Wait () Methode
Die Wait () -Methode ist eine Methode in der Objektklasse; Wenn ein Thread die Wait () -Methode ausführt, tritt er in einen wartenden Pool in Bezug auf das Objekt ein und verliert gleichzeitig die Maschinenschloss des Objekts (verliert vorübergehend die Maschinensperre und die Zeitlimitzeit für Warten (langfristig) muss auch die Objektsperrung zurückgeben). Andere Threads können darauf zugreifen;
Wait () verwendet Benachrichtigung oder Benachrichtigung oder angegebene Schlafzeit, um den Thread im aktuellen Wartepool aufzuwecken.
Wiat () muss in den synchronisierten Block platziert werden, andernfalls wird die Ausnahme von "Java.lang.IilleGalMonitorStateException" zur Laufzeit ausgelöst.
7. Gemeinsame Threadbegriffe Erläuterung
Haupt Thread: Der von dem JVM Call Program Main () generierte Thread.
Aktueller Thread: Dies ist ein verwirrendes Konzept. Bezieht sich im Allgemeinen auf einen Prozess, der über Thread.CurrentThread () erhalten wurde.
Hintergrund -Thread: Bezieht sich auf einen Thread, der Dienste für andere Threads bietet, auch als Daemon -Thread bezeichnet. Der JVM -Müllsammlungsthread ist ein Hintergrund -Thread. Der Unterschied zwischen einem Benutzer -Thread und einem Daemon -Thread besteht darin, ob der Hauptfaden abhängig vom Ende des Haupt -Threads den Vordergrund -Thread beendet: Es bezieht sich auf den Thread, der den Dienst des Hintergrund -Threads akzeptiert. Tatsächlich sind die Vordergrund-Hintergrundfäden miteinander verbunden, genau wie die Beziehung zwischen der Puppe und dem Manipulator hinter den Kulissen. Die Marionette ist der Vordergrundfaden, und der Manipulator hinter den Kulissen ist der Hintergrundfaden. Threads, die von Vordergrundbefäden erstellt wurden, sind standardmäßig auch Vordergrundthreads. Sie können iSdaemon () und setdaemon () -Methoden verwenden, um festzustellen, ob ein Thread ein Hintergrund -Thread ist.
Einige gängige Methoden von Thread -Klassen:
Sleep (): Erzwingen Sie einen Faden zum Schlafen in N -Millisekunden.
ISALIVE (): Bestimmt, ob ein Thread überlebt.
Join (): Warten Sie, bis der Thread endet.
activeCount (): Die Anzahl der aktiven Threads im Programm.
Enrumerate (): Aufzählende Threads im Programm.
CurrentThread (): Erhält den aktuellen Thread.
isdaemon (): ob ein Thread ein Daemon -Thread ist.
setdaemon (): Stellen Sie einen Thread als Daemon -Thread ein. (Der Unterschied zwischen einem Benutzer -Thread und einem Daemon -Thread besteht darin, ob das Haupt -Thread abhängig vom Ende des Haupt -Threads endet.)
setName (): Legen Sie einen Namen für den Thread fest.
Warten Sie (): Zwingen Sie einen Faden zum Warten.
Benachrichtigen (): Benachrichtigen Sie einen Thread, um weiter zu laufen.
setPriority (): Legt die Priorität eines Threads fest.
8. Fadensynchronisation
1. Es gibt zwei Bereiche synchronisierter Schlüsselwörter:
1) Es befindet sich innerhalb einer Objektinstanz. Synchronisierte Amethod () {} kann verhindern, dass mehrere Threads gleichzeitig auf die synchronisierte Methode dieses Objekts zugreifen (wenn ein Objekt mehrere synchronisierte Methoden aufweist, sofern ein Thread gleichzeitig auf eine der synchronisierten Methoden zugreift, können andere Threads nicht auf synchronisierte Methoden zugreifen). Zu diesem Zeitpunkt ist die synchronisierte Methode verschiedener Objektinstanzen ununterbrochen. That is to say, other threads can still access the synchronized method in another object instance of the same class at the same time;
2)是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。
2、除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){/*区块*/},它的作用域是当前对象;
3、synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法;
Java对多线程的支持与同步机制深受大家的喜爱,似乎看起来使用了synchronized关键字就可以轻松地解决多线程共享数据同步问题。到底如何?还得对synchronized关键字的作用进行深入了解才可定论。
总的说来,synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。如果再细的分类,synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。
在进一步阐述之前,我们需要明确几点:
A.无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁而且同步方法很可能还会被其他线程的对象访问。
B.每个对象只有一个锁(lock)与之相关联。
C.实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
接着来讨论synchronized用到不同地方对代码产生的影响:
假设P1、P2是同一个类的不同对象,这个类中定义了以下几种情况的同步块或同步方法,P1、P2就都可以调用它们。
1. 把synchronized当作函数修饰符时,示例代码如下:
Public synchronized void methodAAA(){//….}这也就是同步方法,那这时synchronized锁定的是哪个对象呢?它锁定的是调用这个同步方法对象。也就是说,当一个对象P1在不同的线程中执行这个同步方法时,它们之间会形成互斥,达到同步的效果。但是这个对象所属的Class所产生的另一对象P2却可以任意调用这个被加了synchronized关键字的方法。
上边的示例代码等同于如下代码:
public void methodAAA(){synchronized (this) // (1){ //…..}}(1)处的this指的是什么呢?它指的就是调用这个方法的对象,如P1。可见同步方法实质是将synchronized作用于object reference。那个拿到了P1对象锁的线程,才可以调用P1的同步方法,而对P2而言,P1这个锁与它毫不相干,程序也可能在这种情形下摆脱同步机制的控制,造成数据混乱:(
2.同步块,示例代码如下:
public void method3(SomeObject so) { synchronized(so){ //…..}}这时,锁就是so这个对象,谁拿到这个锁谁就可以运行它所控制的那段代码。当有一个明确的对象作为锁时,就可以这样写程序,但当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的instance变量(它得是一个对象)来充当锁:
class Foo implements Runnable{ private byte[] lock = new byte[0]; // 特殊的instance变量Public void methodA(){ synchronized(lock) { //… }}//…..}注:零长度的byte数组对象创建起来将比任何对象都经济查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。
3.将synchronized作用于static 函数,示例代码如下:
Class Foo{public synchronized static void methodAAA() // 同步的static 函数{//….}public void methodBBB(){ synchronized(Foo.class) // class literal(类名称字面常量)} }代码中的methodBBB()方法是把class literal作为锁的情况,它和同步的static函数产生的效果是一样的,取得的锁很特别,是当前调用这个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了)。
记得在《Effective Java》一书中看到过将Foo.class和P1.getClass()用于作同步锁还不一样,不能用P1.getClass()来达到锁这个Class的目的。P1指的是由Foo类产生的对象。
可以推断:如果一个类中定义了一个synchronized的static函数A,也定义了一个synchronized 的instance函数B,那么这个类的同一对象Obj在多线程中分别访问A和B两个方法时,不会构成同步,因为它们的锁都不一样。A方法的锁是Obj这个对象,而B的锁是Obj所属的那个Class。
1、线程同步的目的是为了保护多个线程反问一个资源时对资源的破坏。
2、线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他非同步方法。
3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。
4、对于同步,要时刻清醒在哪个对象上同步,这是关键。
5、编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。
6、当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。
7、死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小。真让你写个死锁程序,不一定好使,呵呵。但是,一旦程序发生死锁,程序将死掉。
九、线程数据传递
在传统的同步开发模式下,当我们调用一个函数时,通过这个函数的参数将数据传入,并通过这个函数的返回值来返回最终的计算结果。但在多线程的异步开发模式下,数据的传递和返回和同步开发模式有很大的区别。由于线程的运行和结束是不可预料的,因此,在传递和返回数据时就无法象函数一样通过函数参数和return语句来返回数据。
9.1. Passing data through construction methods. When creating a thread, an instance of the Thread class or its subclass must be established. Therefore, it is not difficult to think of passing data into a thread through the constructor of the thread class before calling the start method. And save the incoming data using class variables for thread use (in fact, it is used in run methods). The following code demonstrates how to pass data through constructors:
package mythread; public class MyThread1 extends Thread { private String name; public MyThread1(String name) { this.name = name; } public void run() { System.out.println("hello " + name); } public static void main(String[] args) { Thread thread = new MyThread1("world"); thread.start(); }}由于这种方法是在创建线程对象的同时传递数据的,因此,在线程运行之前这些数据就就已经到位了,这样就不会造成数据在线程运行后才传入的现象。如果要传递更复杂的数据,可以使用集合、类等数据结构。使用构造方法来传递数据虽然比较安全,但如果要传递的数据比较多时,就会造成很多不便。由于Java没有默认参数,要想实现类似默认参数的效果,就得使用重载,这样不但使构造方法本身过于复杂,又会使构造方法在数量上大增。因此,要想避免这种情况,就得通过类方法或类变量来传递数据。
9.2. Pass data through variables and methods
向对象中传入数据一般有两次机会,第一次机会是在建立对象时通过构造方法将数据传入,另外一次机会就是在类中定义一系列的public的方法或变量(也可称之为字段)。然后在建立完对象后,通过对象实例逐个赋值。下面的代码是对MyThread1类的改版,使用了一个setName方法来设置name变量:
package mythread; public class MyThread2 implements Runnable { private String name; public void setName(String name) { this.name = name; } public void run() { System.out.println("hello " + name); } public static void main(String[] args) { MyThread2 myThread = new MyThread2(); myThread.setName("world"); Thread thread = new Thread(myThread); thread.start(); }} 9.3、通过回调函数传递数据
上面讨论的两种向线程中传递数据的方法是最常用的。但这两种方法都是main方法中主动将数据传入线程类的。这对于线程来说,是被动接收这些数据的。然而,在有些应用中需要在线程运行的过程中动态地获取数据,如在下面代码的run方法中产生了3个随机数,然后通过Work类的process方法求这三个随机数的和,并通过Data类的value将结果返回。从这个例子可以看出,在返回value之前,必须要得到三个随机数。也就是说,这个value是无法事先就传入线程类的。
package mythread; class Data { public int value = 0; } class Work { public void process(Data data, Integer numbers) { for (int n : numbers) { data.value += n; } } } public class MyThread3 extends Thread { private Work work; public MyThread3(Work work) { this.work = work; } public void run() { java.util.Random random = new java.util.Random(); Data data = new Data(); int n1 = random.nextInt(1000); int n2 = random.nextInt(2000); int n3 = random.nextInt(3000); work.process(data, n1, n2, n3); // Use the callback function System.out.println(String.valueOf(n1) + "+" + String.valueOf(n2) + "+" + String.valueOf(n3) + "=" + data.value); } public static void main(String[] args) { Thread thread = new MyThread3(new Work()); thread.start(); }}The above is a detailed explanation of Java multi-threading. I hope it can help you learn this part of the knowledge. Vielen Dank für Ihre Unterstützung für diese Website!