Wir alle wissen, dass Programmierer vor JDK1.5, als Business-Parallelität in Java implementiert werden sollte, die Code-Implementierung normalerweise unabhängig abschließen mussten. Natürlich gibt es einige Open-Source-Frameworks, die diese Funktionen bereitstellen, aber diese sind immer noch nicht so nützlich als die Funktionen, die mit JDK kommen. Beim Entwerfen hochwertiger gleichzeitiger Java-Multithread-Programme müssen häufig Leistung, Deadlock und Fairness berücksichtigt werden, um Phänomene wie tote Sprünge wie wait (), notify () und Synchronisierung vor der Verwendung von Java zu verhindern. Viele Faktoren wie die Verwaltung und die Vermeidung von durch Thread-Sicherheit verursachten Schäden übernehmen häufig komplexere Sicherheitsstrategien, was die Entwicklungslast für Programmierer erhöht. Glücklicherweise hat Sun Master (Doug Lea) hat schließlich das Toolkit java.util.concurrent eingeführt, um die gleichzeitige Vervollständigung für uns arme kleine Programmierer zu vereinfachen. Entwickler können dies nutzen, um Race Conditions und Deadlock-Threads wirksam zu reduzieren. Das Concurrent-Paket löst diese Probleme sehr gut und bietet uns ein praktischeres Concurrent-Programmmodell.
Executor: Der Executor einer bestimmten ausführbaren Aufgabe.
ExecutorService: Ein Thread-Pool-Manager, es gibt viele Implementierungsklassen, ich werde einige davon vorstellen. Wir können Runnable und Callable zur Planung an den Pool senden.
Semaphor: ein zählender Semaphor
ReentrantLock: Eine wiedereintretende, sich gegenseitig ausschließende Sperre, die in ihrer Funktion der synchronisierten Sperre ähnelt, aber viel leistungsfähiger ist.
Zukunft: Es handelt sich um eine Schnittstelle für die Interaktion mit Runnable und Callable, z. B. zum Abrufen des zurückgegebenen Ergebnisses nach der Ausführung eines Threads usw. Außerdem bietet es Abbrechen, um den Thread zu beenden.
BlockingQueue: Blockierungswarteschlange.
CompletionService: Erweiterung von ExecutorService, die Thread-Ausführungsergebnisse erhalten kann
CountDownLatch: Eine Synchronisierungs-Hilfsklasse, die es einem oder mehreren Threads ermöglicht, zu warten, bis eine Reihe von Vorgängen abgeschlossen ist, die in anderen Threads ausgeführt werden.
CyclicBarrier: eine Synchronisierungs-Hilfsklasse, die es einer Gruppe von Threads ermöglicht, aufeinander zu warten, bis ein gemeinsamer Barrierepunkt erreicht ist
Zukunft: Zukunft stellt das Ergebnis der asynchronen Berechnung dar.
ScheduledExecutorService: Ein ExecutorService, der die Ausführung von Befehlen nach einer bestimmten Verzögerung oder in regelmäßigen Abständen plant.
Als nächstes werden wir sie einzeln vorstellen
Beschreibung der Hauptmethode des Ausführenden
newFixedThreadPool (Thread-Pool fester Größe)
Erstellen Sie einen Thread-Pool, der einen festen Satz von Threads wiederverwenden kann, und führen Sie diese Threads in einer gemeinsam genutzten, unbegrenzten Warteschlange aus (nur die angeforderten Threads warten in einer Warteschlange auf die Ausführung). Wenn ein Thread aufgrund eines Fehlers während der Ausführung vor dem Herunterfahren beendet wird, führt ein neuer Thread an seiner Stelle nachfolgende Aufgaben aus (falls erforderlich).
newCachedThreadPool (unbegrenzter Thread-Pool, kann automatisches Thread-Recycling durchführen)
Erstellt einen Thread-Pool, der bei Bedarf neue Threads erstellt, zuvor erstellte Threads jedoch wiederverwendet, sobald sie verfügbar sind. Bei Programmen, die viele kurzlebige asynchrone Aufgaben ausführen, verbessern diese Thread-Pools häufig die Programmleistung. Durch den Aufruf von „execute“ wird der zuvor erstellte Thread wiederverwendet (sofern der Thread verfügbar ist). Wenn kein vorhandener Thread verfügbar ist, wird ein neuer Thread erstellt und dem Pool hinzugefügt. Beenden und entfernen Sie die Threads aus dem Cache, die 60 Sekunden lang nicht verwendet wurden. Daher verbraucht ein Thread-Pool, der längere Zeit im Leerlauf bleibt, keine Ressourcen. Beachten Sie, dass Sie den ThreadPoolExecutor-Konstruktor verwenden können, um einen Thread-Pool mit ähnlichen Eigenschaften, aber unterschiedlichen Details (z. B. Timeout-Parameter) zu erstellen.
newSingleThreadExecutor (einzelner Hintergrundthread)
Erstellen Sie einen Executor, der einen einzelnen Arbeitsthread verwendet und den Thread in einer unbegrenzten Warteschlange ausführt. (Beachten Sie, dass, wenn dieser einzelne Thread aufgrund eines Fehlers während der Ausführung vor dem Herunterfahren beendet wird, an seiner Stelle bei Bedarf ein neuer Thread nachfolgende Aufgaben ausführt.) Es ist garantiert, dass Aufgaben nacheinander ausgeführt werden und es zu keinem Zeitpunkt mehr als einen Thread aktiv sein wird. Im Gegensatz zum entsprechenden newFixedThreadPool(1) ist der von dieser Methode zurückgegebene Executor garantiert in der Lage, andere Threads zu verwenden, ohne ihn neu zu konfigurieren.
Diese Methoden geben ExecutorService-Objekte zurück, die als Thread-Pool verstanden werden können.
Die Funktion dieses Thread-Pools ist relativ vollständig. Sie können Aufgaben mit „submit()“ übermitteln und den Thread-Pool mit „shutdown()“ beenden.
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class MyExecutor erweitert Thread {private int index;public MyExecutor(int i){ this.index=i;}public void run(){ try{ System.out.println("["+this.index+"] start...."); Thread.sleep((int)(Math.random()*)); System.out.println("["+this.index+"] end."); }}public static void main(String args[]){ ExecutorService service=Executors.newFixedThreadPool(); for(int i=;i<;i++){ service.execute(new MyExecutor(i)); //service.submit(new MyExecutor(i)); } System.out.println("submit finish");
Obwohl einige Informationen gedruckt werden, ist nicht ganz klar, wie dieser Thread-Pool funktioniert. Erhöhen wir die Ruhezeit um das Zehnfache.
Thread.sleep((int)(Math.random()*10000));
Wenn Sie weiter schauen, werden Sie deutlich erkennen, dass nur 4 Threads ausgeführt werden können. Wenn ein Thread ausgeführt wird, wird ein neuer Thread ausgeführt. Das heißt, nachdem wir alle Threads übermittelt haben, wartet der Thread-Pool auf die endgültige Ausführung. Wir werden auch feststellen, dass der einreichende Thread in einer „unbegrenzten Warteschlange“ platziert wird. Dies ist eine geordnete Warteschlange (BlockingQueue, die weiter unten erläutert wird).
Darüber hinaus wird die statische Funktion von Executors verwendet, um einen festen Thread-Pool zu generieren. Wie der Name schon sagt, wird der Thread im Thread-Pool nicht freigegeben, selbst wenn er sich im Leerlauf befindet.
Dies führt zu Leistungsproblemen. Wenn beispielsweise die Größe des Thread-Pools 200 beträgt und alle Threads verwendet werden, bleiben alle Threads weiterhin im Pool und die entsprechende Speicher- und Thread-Umschaltung (while(true)+sleep-Schleife). ) wird zunehmen.
Wenn Sie dieses Problem vermeiden möchten, müssen Sie ThreadPoolExecutor () direkt zum Erstellen verwenden. Sie können die „maximale Anzahl an Threads“, die „minimale Anzahl an Threads“ und die „KeepAlive-Zeit für Threads im Leerlauf“ wie bei einem allgemeinen Thread-Pool festlegen.
Dies ist die grundlegende Verwendung des Thread-Pools.
Semaphor
Ein zählendes Semaphor. Konzeptionell verwaltet ein Semaphor eine Sammlung von Berechtigungen. Bei Bedarf wird jeder acquire() blockiert, bis die Berechtigung verfügbar ist, und dann wird die Berechtigung erworben. Jedes release() fügt eine Berechtigung hinzu und gibt möglicherweise einen blockierenden Acquirer frei. Anstatt jedoch tatsächliche Lizenzobjekte zu verwenden, zählt Semaphore lediglich die Anzahl der verfügbaren Lizenzen und ergreift entsprechende Maßnahmen.
Semaphor wird häufig verwendet, um die Anzahl der Threads zu begrenzen, die auf bestimmte Ressourcen (physische oder logische) zugreifen können. Die folgende Klasse verwendet beispielsweise Semaphoren, um den Zugriff auf einen Inhaltspool zu steuern:
Hier ist eine reale Situation. Alle stehen Schlange, um auf die Toilette zu gehen. Es gibt nur zwei Plätze auf der Toilette.
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Semaphore;public class MySemaphore erweitert Thread {Semaphore position;private int id;public MySemaphore(int i,Semaphore s){ this.id=i; this.position=s;}public void run(){ try{ if(position.availablePermits()>){ System.out.println("Customer["+this.id+"] betritt die Toilette, da ist Platz"); } else{ System.out.println("Customer["+ this.id+"] betritt die Toilette, kein Platz, Warteschlange"); } position.acquire("Kunde ["+this.id+"] erwirbt einen Boxenplatz"); Thread.sleep((int)(Math.random()*)); System.out.println("Der Kunde ["+this.id+"] hat die Verwendung beendet"); ) { e.printStackTrace(); }} public static void main(String args[]){ ExecutorService list=Executors.newCachedThreadPool(); Semaphore(); for(int i=;i<;i++){ list.submit(new MySemaphore(i+,position)); position.acquireUninterruptably("Use Fertig, muss aufgeräumt werden"); position.release();}}
ReentrantLock
Eine wiedereintrittsfähige Mutex-Sperre, die zum Teil das gleiche grundlegende Verhalten und die gleiche Semantik aufweist wie die implizite Monitorsperre, auf die über synchronisierte Methoden und Anweisungen zugegriffen wird, jedoch leistungsfähiger ist.
Ein ReentrantLock gehört dem Thread, der die Sperre zuletzt erfolgreich erworben und die Sperre noch nicht freigegeben hat. Wenn die Sperre nicht einem anderen Thread gehört, erhält der Thread, der die Sperre aufruft, die Sperre erfolgreich und kehrt zurück. Wenn der aktuelle Thread die Sperre bereits hält, kehrt diese Methode sofort zurück. Sie können die Methoden isHeldByCurrentThread() und getHoldCount() verwenden, um zu überprüfen, ob dies auftritt.
Der Konstruktor dieser Klasse akzeptiert einen optionalen Fairness-Parameter.
Wenn diese Sperren auf „true“ gesetzt sind und mehrere Threads im Konflikt stehen, gewähren sie tendenziell Zugriff auf den Thread, der am längsten gewartet hat. Andernfalls garantiert diese Sperre keine bestimmte Zugriffsreihenfolge.
Verglichen mit der Standardeinstellung (mit unfairem Sperren) hat ein Programm, das faires Sperren verwendet, einen sehr geringen Gesamtdurchsatz (d. h. es ist sehr langsam, oft extrem langsam), wenn viele Threads darauf zugreifen, weist jedoch eine schlechte Leistung beim Erwerb von Sperren auf und garantierte Sperrenzuteilungen. Der Unterschied ist gering, wenn es um das Gleichgewicht geht.
Es ist jedoch zu beachten, dass eine faire Sperre nicht die Fairness der Thread-Planung garantiert. Daher kann es sein, dass einer von vielen Threads, die eine faire Sperre verwenden, mehrere Erfolgsaussichten hat, was der Fall ist, wenn andere aktive Threads nicht verarbeitet werden und die Sperre derzeit nicht halten.
Beachten Sie außerdem, dass die nicht zeitgesteuerte tryLock-Methode keine Fairness-Einstellungen verwendet. Denn diese Methode kann erfolgreich sein, solange die Sperre verfügbar ist, auch wenn andere Threads warten.
Es wird empfohlen, immer sofort zu üben und einen Try-Block zum Aufrufen der Sperre zu verwenden. In der Vorher/Nachher-Konstruktion lautet der typischste Code wie folgt:
class unlock() } }}
Mein Beispiel:
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.locks.ReentrantLock;public class MyReentrantLock erweitert Thread{TestReentrantLock lock;private int id;public MyReentrantLock(int i,TestReentrantLock test ){ this.id=i; this.lock=test;}public void run(){ lock.print(id);}public static void main(String args[]){ ExecutorService service=Executors.newCachedThreadPool(); TestReentrantLock lock=new TestReentrantLock(); for(int i=;i<;i++){ service. subscribe(new MyReentrantLock(i,lock)); } service.shutdown();}}class TestReentrantLock{private ReentrantLock lock=new ReentrantLock(); public void print(int str){ try{ lock.lock(); Thread.sleep((int)(Math.random()* ));
BlockingQueue
Eine Warteschlange, die zwei zusätzliche Vorgänge unterstützt: Warten darauf, dass die Warteschlange nicht leer wird, wenn ein Element abgerufen wird, und Warten darauf, dass Speicherplatz verfügbar wird, wenn ein Element gespeichert wird.
BlockingQueue akzeptiert keine Nullelemente. Einige Implementierungen lösen eine NullPointerException aus, wenn sie versuchen, ein Nullelement hinzuzufügen, einzufügen oder anzubieten. null wird als Warnwert verwendet, um anzuzeigen, dass der Abfragevorgang fehlgeschlagen ist.
BlockingQueue kann kapazitätsbegrenzt sein. Es kann jederzeit über eine verbleibende Kapazität verfügen, über die hinaus keine weiteren Elemente ohne Blockierung hinzugefügt werden können.
Eine BlockingQueue ohne interne Kapazitätsbeschränkungen meldet immer Integer.MAX_VALUE der verbleibenden Kapazität.
Die BlockingQueue-Implementierung wird hauptsächlich für Producer-Consumer-Warteschlangen verwendet, unterstützt aber zusätzlich die Collection-Schnittstelle. So ist es beispielsweise möglich, mit „remove(x)“ ein beliebiges Element aus der Warteschlange zu entfernen.
Dieser Vorgang wird jedoch normalerweise nicht effizient durchgeführt und kann nur gelegentlich und in geplanter Weise verwendet werden, beispielsweise beim Entfernen einer Nachricht aus der Warteschlange.
Die BlockingQueue-Implementierung ist threadsicher. Alle Warteschlangenmethoden können interne Sperren oder andere Formen der Parallelitätskontrolle verwenden, um ihre Zwecke automatisch zu erreichen.
Eine große Anzahl von Sammlungsvorgängen (addAll, containsAll, retainAll und removeAll) werden jedoch nicht unbedingt automatisch ausgeführt, sofern dies nicht ausdrücklich in der Implementierung angegeben ist.
So könnte beispielsweise addAll(c) fehlschlagen (eine Ausnahme auslösen), nachdem nur einige Elemente in c hinzugefügt wurden.
BlockingQueue unterstützt im Wesentlichen keine Art von „Schließen“- oder „Herunterfahren“-Vorgängen, um anzuzeigen, dass keine weiteren Elemente hinzugefügt werden.
Der Bedarf und die Verwendung dieser Funktionalität hängen in der Regel von der Implementierung ab. Eine gängige Strategie besteht beispielsweise darin, spezielle End-of-Stream- oder Poison-Objekte in den Produzenten einzufügen und sie basierend darauf zu interpretieren, wann der Verbraucher sie erhält.
Das folgende Beispiel demonstriert die Grundfunktionalität dieser Blockierungswarteschlange.
import java.util.concurrent.BlockingQueue;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.LinkedBlockingQueue;public class MyBlockingQueue erweitert Thread {public static BlockingQueue<String> queue = new LinkedBlockingQueue<String>();private int index;public MyBlockingQueue(int i) { this.index = i;} public void run() { try { queue.put(String.valueOf(this.index)); System.out.println("{" + this.index + " } in queue!"); } Catch (Exception e) { e.printStackTrace(); }} public static void main(String args[]) { ExecutorService service = Executors.newCachedThreadPool(); for (int i = ; i < ; i++) { service.submit(new MyBlockingQueue(i)); } Thread thread = new Thread() { public void run() { try { while (true) { Thread.sleep((int) (Math.random() * )); if(MyBlockingQueue.queue.isEmpty()) break; MyBlockingQueue.take(); System.out.println(str + " has take!"); abschalten();}}
------------Ausführungsergebnisse-----------------
{0} in der Warteschlange!
{1} in der Warteschlange!
{2} in der Warteschlange!
{3} in der Warteschlange!
0 hat vergeben!
{4} in der Warteschlange!
1 hat genommen!
{6} in der Warteschlange!
2 hat genommen!
{7} in der Warteschlange!
3 hat genommen!
{8} in der Warteschlange!
4 hat genommen!
{5} in der Warteschlange!
6 hat genommen!
{9} in der Warteschlange!
7 hat genommen!
8 hat genommen!
5 hat genommen!
9 hat genommen!
----------------------------------------
CompletionService
Ein Dienst, der die Erstellung neuer asynchroner Aufgaben vom Konsumieren der Ergebnisse abgeschlossener Aufgaben trennt. Der Produzent übermittelt die auszuführende Aufgabe. Der Benutzer übernimmt erledigte Aufgaben und verarbeitet deren Ergebnisse in der Reihenfolge, in der sie erledigt wurden. Beispielsweise kann ein CompletionService zur Verwaltung asynchroner E/A verwendet werden. Die Aufgabe, einen Lesevorgang auszuführen, wird als Teil des Programms oder Systems übermittelt. Wenn der Lesevorgang abgeschlossen ist, werden andere Vorgänge in einem anderen Teil des Programms ausgeführt , möglicherweise in der Reihenfolge, in der die Vorgänge angefordert wurden. Die Reihenfolge ist unterschiedlich.
Normalerweise ist ein CompletionService auf einen separaten Executor angewiesen, um die Aufgabe tatsächlich auszuführen. In diesem Fall verwaltet der CompletionService nur eine interne Abschlusswarteschlange. Die ExecutorCompletionService-Klasse stellt eine Implementierung dieser Methode bereit.
import java.util.concurrent.Callable;import java.util.concurrent.CompletionService;import java.util.concurrent.ExecutorCompletionService;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class MyCompletionService implementiert Callable <String> {private int id;public MyCompletionService(int i){ this.id=i;}public static void main(String[] args) throws Exception{ ExecutorService service=Executors.newCachedThreadPool(); completion=new ExecutorCompletionService<String>(service); i<;i++){ completion.submit(new MyCompletionService(i)); } for(int i=;i<;i++){ System.out.println(completion.take().get()); } service.shutdown();} public String call() throws Exception { Integer time=(int)(Math.random()*); System.out.println(this.id+" start"); Thread.sleep(time); System.out.println(this.id+" end"); e.printStackTrace(); } return this.id+":"+time;}}
CountDownLatch
Eine Synchronisierungs-Hilfsklasse, die es einem oder mehreren Threads ermöglicht, zu warten, bis eine Reihe von Vorgängen abgeschlossen ist, die in anderen Threads ausgeführt werden.
Initialisiert CountDownLatch mit der angegebenen Anzahl. Da die Methode „countDown()“ aufgerufen wird, blockiert die Methode „await“, bis der aktuelle Zählerstand Null erreicht.
Anschließend werden alle wartenden Threads freigegeben und alle nachfolgenden Aufrufe von waiting kehren sofort zurück. Dieses Verhalten tritt nur einmal auf – der Zähler kann nicht zurückgesetzt werden. Wenn Sie die Zählung zurücksetzen müssen, ziehen Sie die Verwendung von CyclicBarrier in Betracht.
CountDownLatch ist ein allgemeines Synchronisierungstool, das viele Verwendungsmöglichkeiten bietet. Verwenden Sie einen CountDownLatch, der mit count 1 initialisiert wurde, als einfachen Ein-/Aus-Latch oder Eintrag: Alle Threads, die „await“ aufrufen, warten am Eintrag, bis der Eintrag durch den Thread geöffnet wird, der countDown() aufruft.
Ein mit N initialisierter CountDownLatch kann dazu führen, dass ein Thread wartet, bis N Threads einen Vorgang abgeschlossen haben, oder dass er wartet, bis ein Vorgang N-mal abgeschlossen wurde.
Eine nützliche Funktion von CountDownLatch besteht darin, dass der Thread, der die countDown-Methode aufruft, nicht warten muss, bis die Zählung Null erreicht, bevor er fortfährt, sondern vielmehr verhindert, dass ein Thread durch einen Wartevorgang fortfährt, bis alle Threads passieren können.
Das folgende Beispiel wurde von jemand anderem geschrieben und ist sehr anschaulich.
import java.util.concurrent.CountDownLatch;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class TestCountDownLatch {public static void main(String[] args) throws InterruptedException { // Start der Countdown-Sperre final CountDownLatch begin = new CountDownLatch(); // Ende der Countdown-Sperre final CountDownLatch end = new CountDownLatch(); // Zehn Teilnehmer final ExecutorService exec = Executors.newFixedThreadPool(); for (int index = ; index < ; index++) { Final int NO = new Runnable() { public void run( ) { try { begin.await();//blockiere immer Thread.sleep((long) (Math.random() * )); System.out.println("No." + NO + "arrived"); println("Game Start"); begin.countDown(); end.await(); System.out.println("Exec.shutdown();}}
Die wichtigsten Methoden von CountDownLatch sind countDown() undwait(). Ersteres zählt hauptsächlich einmal herunter, und letzteres wartet auf den Countdown bis 0. Wenn es 0 nicht erreicht, wird es nur blockiert und gewartet.
CyclicBarrier
Eine Synchronisierungs-Hilfsklasse, die es einer Gruppe von Threads ermöglicht, aufeinander zu warten, bis ein gemeinsamer Barrierepunkt erreicht ist.
CyclicBarrier ist in Programmen nützlich, die eine Reihe von Threads fester Größe umfassen, die von Zeit zu Zeit aufeinander warten müssen. Da die Barriere nach der Freigabe des wartenden Threads wiederverwendet werden kann, wird sie als zyklische Barriere bezeichnet.
CyclicBarrier unterstützt einen optionalen Runnable-Befehl, der nur einmal an jedem Barrierepunkt ausgeführt wird, nachdem der letzte Thread in einer Reihe von Threads angekommen ist (jedoch bevor alle Threads freigegeben werden). Dieser Barrierevorgang ist nützlich, wenn der gemeinsame Status aktualisiert wird, bevor alle teilnehmenden Threads fortgesetzt werden.
Beispielverwendung: Das Folgende ist ein Beispiel für die Verwendung von Barrieren im parallelen Zerlegungsdesign, ein sehr klassisches Beispiel für eine Reisegruppe:
import java.text.SimpleDateFormat;import java.util.Date;import java.util.concurrent.BrokenBarrierException;import java.util.concurrent.CyclicBarrier;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors; öffentliche Klasse TestCyclicBarrier { // Zeitaufwand für Wanderungen: Shenzhen, Guangzhou, Shaoguan, Changsha, Wuhan private static int[] timeWalk = { , , , , , }; // Selbstfahrende Tour private static int[] timeSelf = { , , , , }; // Tourbus private static int[] timeBus = { , , , , } ; static String now() { SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); sdf.format(new Date()) + ": "; static class Tour implementiert Runnable { private int[] times; private CyclicBarrier barrier; this.times = times; this.tourName = tourName; this.barrier = barrier } public void run() { try { Thread.sleep(times[] * ); System.out.println(now() + tourName + „Shenzhen erreicht“); barrier.await(); Thread.sleep(times[] * ); System.out.println(now() + tourName + „Guangzhou erreicht“ ); barrier.await(); Thread.sleep(times[] * ); System.out.println(now() + tourName + "Shaoguan erreicht"); barrier.await(); Thread.sleep(times[] * ); System.out.println(now() + tourName + " Barriere.await(times[] * ); System.out.println(now() + tourName + „Wuhan erreicht“); barrier.await(); (BrokenBarrierException e) { } } } public static void main(String[] args) { // Drei Tourgruppen CyclicBarrier barrier = new CyclicBarrier(); ExecutorService exec = Executors.newFixedThreadPool(); "WalkTour", timeWalk)); exec.submit(new Tour(barrier, "SelfTour", timeSelf));//Wenn wir den folgenden Code auskommentieren, werden wir feststellen, dass das Programm blockiert ist und nicht weiter ausgeführt werden kann. exec.submit(new Tour(barrier, "BusTour", timeBus)); exec.shutdown( }}
Das wichtigste Attribut von CyclicBarrier ist die Anzahl der Teilnehmer, und die wichtigste Methode istwait(). Wenn alle Threads „await()“ aufgerufen haben, bedeutet dies, dass diese Threads weiterhin ausgeführt werden können, andernfalls warten sie.
Zukunft
Future stellt das Ergebnis der asynchronen Berechnung dar. Es bietet Methoden zum Überprüfen, ob die Berechnung abgeschlossen ist, zum Warten auf den Abschluss der Berechnung und zum Abrufen des Berechnungsergebnisses.
Nach Abschluss der Berechnung kann nur die Get-Methode zum Abrufen der Ergebnisse verwendet werden. Bei Bedarf kann diese Methode vor Abschluss der Berechnung blockiert werden. Die Stornierung erfolgt über die Methode cancel.
Es stehen zusätzliche Methoden zur Verfügung, um festzustellen, ob eine Aufgabe normal abgeschlossen wurde oder abgebrochen wurde. Sobald eine Berechnung abgeschlossen ist, kann sie nicht mehr abgebrochen werden.
Wenn Sie eine Zukunft zur Abbruchbarkeit verwenden, aber kein brauchbares Ergebnis liefern, können Sie einen formalen Typ „Future<?>“ deklarieren und als Ergebnis der zugrunde liegenden Aufgabe null zurückgeben.
Wir haben dies bereits in CompletionService gesehen, die Funktion dieser Zukunft, und diese kann beim Senden des Threads als Rückgabeobjekt angegeben werden.
ScheduledExecutorService
Ein ExecutorService, der die Ausführung von Befehlen nach einer bestimmten Verzögerung oder in regelmäßigen Abständen plant.
Die Schedule-Methode erstellt Aufgaben mit verschiedenen Verzögerungen und gibt ein Aufgabenobjekt zurück, mit dem die Ausführung abgebrochen oder überprüft werden kann. Die Methoden „scheduleAtFixedRate“ und „scheduleWithFixedDelay“ erstellen und führen bestimmte Aufgaben aus, die regelmäßig ausgeführt werden, bis sie abgebrochen werden.
Mit Executor.execute(java.lang.Runnable) und der Submit-Methode von ExecutorService übermittelte Befehle werden mit der angeforderten Verzögerung von 0 geplant.
Null- und negative Verzögerungen (jedoch keine Zeiträume) sind in der Zeitplanmethode zulässig und werden als Anforderungen behandelt, die sofort ausgeführt werden müssen.
Alle Zeitplanmethoden akzeptieren relative Verzögerungen und Zeiträume als Parameter anstelle absoluter Zeiten oder Daten. Es ist einfach, die durch ein Datum dargestellte absolute Zeit in die erforderliche Form umzuwandeln.
Um beispielsweise eine Ausführung zu einem späteren Zeitpunkt zu planen, verwenden Sie: Schedule(task, date.getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS).
Beachten Sie jedoch, dass das relativ verzögerte Ablaufdatum aufgrund von Netzwerkzeitsynchronisierungsprotokollen, Uhrabweichungen oder anderen Faktoren nicht mit dem aktuellen Datum der aktivierten Aufgabe übereinstimmen muss.
Die Executors-Klasse stellt praktische Factory-Methoden für die in diesem Paket bereitgestellte ScheduledExecutorService-Implementierung bereit.
Die folgenden Beispiele sind auch im Internet beliebt.
import static java.util.concurrent.TimeUnit.SECONDS;import java.util.Date;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.ScheduledFuture;public class TestScheduledThread { public static void main(String[] args) { final ScheduledExecutorService Scheduler = Executors.newScheduledThreadPool(); final Runnable() { int count = ; public void run() { System.out.println(new Date() + " beep " + (++count)); // Nach Sekunden und jede Sekunde ausführen final ScheduledFuture beeperHandle = schemer.scheduleAtFixedRate(beeper, , , SECONDS); // Nach Sekunden ausführen und Sekunden warten, nachdem die letzte Aufgabe ausgeführt wurde, und sie dann jedes Mal erneut ausführen final ScheduledFuture beeperHandle = schemer.scheduleWithFixedDelay(beeper, , , SECONDS); // Beende die Aufgabe nach Sekunden und schließe den Scheduler-Scheduler . Schedule(new Runnable() { public void run() { beeperHandle.cancel(true); beeperHandle.cancel(true); Scheduler.shutdown(); } }, , SECONDS);}}
Auf diese Weise haben wir die wichtigsten Funktionen des Concurrent-Pakets zusammengefasst. Wir hoffen, dass es für unser Verständnis hilfreich ist.