Queue ist eine spezielle lineare Tabelle, die dem Prinzip „First in, first out“ folgt. In unserem täglichen Gebrauch verwenden wir es oft, um gleichzeitig Daten zu manipulieren. Bei der gleichzeitigen Programmierung ist es manchmal notwendig, threadsichere Warteschlangen zu verwenden. Wenn Sie eine Thread-sichere Warteschlange implementieren möchten, gibt es normalerweise zwei Möglichkeiten: Eine besteht darin, eine Blockierungswarteschlange zu verwenden, und die andere darin, eine Thread-Synchronisationssperre zu verwenden.
Was ist eine Blockierungswarteschlange?
Angenommen, es gibt eine Bäckerei, in der ein Kunde Brot isst und ein Koch Brot backt. Es sind nicht mehr als 2 Stück Brot im Korb. Nachdem der Koch den Teig beendet hat, legt er das Brot in den Korb und wenn die Gäste das Brot essen, nehmen sie es aus dem Korb, um sicherzustellen, dass es dort ist Ist das Brot im Korb, wenn die Gäste das Brot essen, oder ist der Korb nicht überfüllt, wenn der Koch das Brot backt? Zu diesem Zeitpunkt müssen wir das Konzept der Blockierungswarteschlange einführen, das wir oft als Produzenten-Konsumenten-Modell bezeichnen.
Eine blockierende Warteschlange ist eine Warteschlange, die zwei zusätzliche Vorgänge unterstützt. Diese beiden zusätzlichen Vorgänge unterstützen das Blockieren von Einfügungs- und Entfernungsmethoden.
(1) Blockierende Einfügungsmethode unterstützen: Das heißt, wenn die Warteschlange voll ist, blockiert die Warteschlange den Thread, der Elemente einfügt, bis die Warteschlange nicht mehr voll ist.
(2) Unterstützt die Blockierungsentfernungsmethode: Dies bedeutet, dass der Thread, der das Element erhält, darauf wartet, dass die Warteschlange leer wird, wenn die Warteschlange leer ist. Blockierende Warteschlangen werden häufig in Produzenten- und Verbraucherszenarien verwendet. Der Produzent ist der Thread, der Elemente zur Warteschlange hinzufügt, und der Verbraucher ist der Thread, der Elemente aus der Warteschlange übernimmt. Eine Blockierungswarteschlange ist ein Container, der von Produzenten zum Speichern von Elementen und von Konsumenten zum Abrufen von Elementen verwendet wird.
Keine blockierenden Warteschlangen im System: PriorityQueue und ConcurrentLinkedQueue
Werfen wir einen Blick auf die Beziehung zwischen nicht blockierenden Warteschlangen (am Beispiel von PriorityQueue):
Die PriorityQueue-Klasse erbt von AbstractQueue und implementiert die Serializable-Schnittstelle. PriorityQueue verwaltet im Wesentlichen eine geordnete Liste und befindet sich im Java-Util-Paket. Das Wort Priority in der ersten Hälfte seines Namens bedeutet tatsächlich „Priorität“. Der Warteschlange hinzugefügte Elemente werden entsprechend ihrer natürlichen Reihenfolge (über die java.util.Comparable-Implementierung) oder gemäß der an den Konstruktor übergebenen java.util.Comparator-Implementierung positioniert.
ConcurrentLinkedQueue ist eine threadsichere Warteschlange, die auf verknüpften Knoten basiert. Der gleichzeitige Zugriff erfordert keine Synchronisierung. Da Elemente zum Ende der Warteschlange hinzugefügt und aus dem Kopf entfernt werden, funktioniert der gemeinsame Zugriff von ConcurrentLinkedQueue auf eine gemeinsame Sammlung problemlos, ohne die Größe der Warteschlange zu kennen. Das Sammeln von Informationen über die Warteschlangengröße ist langsam und erfordert das Durchlaufen der Warteschlange. ConcurrentLinkedQueue ist eine unbegrenzte Thread-sichere Warteschlange, die auf verknüpften Knoten basiert. Sie verwendet eine First-In-First-Out-Regel, um die Knoten zu sortieren. Es wird dem Ende der Warteschlange hinzugefügt. Wenn wir ein Element erhalten, wird das Element am Anfang der Warteschlange zurückgegeben.
Warteschlange, die die Blockierungsschnittstelle implementiert:
Die BlockingQueue-Schnittstelle und fünf Blockierungswarteschlangenklassen werden zu java.util.concurrent hinzugefügt. Es handelt sich im Wesentlichen um eine FIFO-Datenstruktur mit einer Besonderheit. Anstatt sofort Elemente zur Warteschlange hinzuzufügen oder daraus zu entfernen, blockiert der Thread, der die Operation ausführt, bis Platz oder ein Element verfügbar wird.
Die fünf Warteschlangen bieten verschiedene:
·ArrayBlockingQueue: Eine begrenzte Warteschlange, die von einem Array unterstützt wird.
·LinkedBlockingQueue: Eine optionale begrenzte Warteschlange, die durch verknüpfte Knoten unterstützt wird.
·PriorityBlockingQueue: Eine unbegrenzte Prioritätswarteschlange, die durch einen Prioritätsheap unterstützt wird.
·DelayQueue: Eine zeitbasierte Planungswarteschlange, die durch einen Prioritätsheap unterstützt wird.
·SynchronousQueue: Ein einfacher Rendezvous-Mechanismus unter Verwendung der BlockingQueue-Schnittstelle.
Werfen wir einen Blick auf die Vererbungsbeziehung zwischen ArrayBlockingQueue und LinkedBlockingQueue:
Indem wir uns die Vererbungsbeziehung zwischen betrachten Wir wissen, dass die beiden Klassen auch von AbstractQueue erben und die Serializable-Schnittstelle implementieren. Der Unterschied besteht darin, dass sie auch die BlockingQueue-Schnittstelle implementieren.
Eine kurze Einführung zu einigen davon:
Die Standardgröße von LinkedBlockingQueueLinkedBlockingQueue ist Integer.MAX_VALUE, was als zwischengespeicherte begrenzte Warteschlange verstanden werden kann. Es handelt sich um eine Warteschlange, die auf einer verknüpften Liste basiert (Wer zuerst rein, zuerst raus). Wenn der Produzent ein Datenelement in die Warteschlange stellt, wird es in der Warteschlange zwischengespeichert. Wenn der Warteschlangenpuffer die maximale Cache-Kapazität erreicht (LinkedBlockingQueue kann diesen Wert über den Konstruktor angeben), wird die Produzentenwarteschlange blockiert, bis der Verbraucher sie verbraucht Wenn ein Datenelement gelöscht wird, wird der Produzenten-Thread aktiviert und umgekehrt.
ArrayBlockingQueue muss beim Erstellen die Kapazität angeben, und Sie können auswählen, ob Fairness erforderlich ist. Wenn der Fairness-Parameter auf true gesetzt ist, wird der Thread mit der längsten Wartezeit zuerst verarbeitet (tatsächlich wird ReentrantLock auf gesetzt). wahr Diese Art von Fairness: Das heißt, der Thread mit der längsten Wartezeit wird zuerst ausgeführt. Im Allgemeinen kostet Fairness Leistung, also nutzen Sie sie nur, wenn Sie sie wirklich brauchen. Es handelt sich um eine Array-basierte blockierende Ringwarteschlange, die Elemente nach dem FIFO-Prinzip (First In First Out) sortiert.
PriorityBlockingQueue ist eine Prioritätswarteschlange, keine First-In-First-Out-Warteschlange. Elemente werden in der Reihenfolge ihrer Priorität entfernt, und die Warteschlange hat keine Obergrenze (nach Betrachtung des Quellcodes ist PriorityBlockingQueue eine Neuverpackung von PriorityQueue, basierend auf der Heap-Datenstruktur, und PriorityQueue hat keine Kapazitätsbeschränkung, genau wie ArrayList in Priorität Es wird nicht blockiert, wenn es in die Blockierungswarteschlange gestellt wird. Obwohl diese Warteschlange logisch unbegrenzt ist, kann der Versuch, einen Add-Vorgang auszuführen, einen OutOfMemoryError verursachen, weil die Ressourcen erschöpft sind Das Element wird blockiert, daher ist sein Abrufvorgang blockiert. Darüber hinaus müssen die in die Warteschlange eingehenden Elemente über Vergleichsmöglichkeiten verfügen.
Über ConcurrentLinkedQueue und LinkedBlockingQueue:
kann auch als Unterschied zwischen blockierender Warteschlange und nicht blockierender Warteschlange verstanden werden:
1. LinkedBlockingQueue verwendet a Der Sperrmechanismus verwendet ConcurrentLinkedQueue den CAS-Algorithmus, obwohl die zugrunde liegende Sperrenerfassung von LinkedBlockingQueue ebenfalls den CAS-Algorithmus verwendet.
2. In Bezug auf das Abrufen von Elementen unterstützt ConcurrentLinkedQueue das Blockieren zum Abrufen von Elementen nicht und LinkedBlockingQueue unterstützt die blockierende take()-Methode.
3. In Bezug auf die Leistung beim Einfügen von Elementen ist der Unterschied zwischen Sperren und Sperren im tatsächlichen Gebrauch viel schneller als bei LinkedBlockingQueue.
Produzenten-Konsumenten-Code:
Ich habe im Internet ein kleines Beispiel eines Produzenten-Konsumenten gesehen, das für das Verständnis von Blockierungswarteschlangen sehr hilfreich ist. Der Code ist wie folgt folgt:
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class BlockingQueueTest { public static class Basket { BlockingQueue<String> basket = new ArrayBlockingQueue<>(3); private void produce() throws InterruptedException { basket.put("苹果"); } private void consume() throws InterruptedException { basket.take(); } private int getAppleNumber() { return basket.size(); } } private static void testBasket() { final Basket basket = new Basket(); class Producer implements Runnable { public void run() { try { while (true) { System.out.println("生产者开始生产苹果###"); basket.produce(); System.out.println("生产者生产苹果完毕###"); System.out.println("篮子中的苹果数量:" + basket.getAppleNumber() + "个"); Thread.sleep(300); } } catch (InterruptedException e) {} } } class Consumer implements Runnable { public void run() { try { while (true) { System.out.println("消费者开始消费苹果***"); basket.consume(); System.out.println("消费者消费苹果完毕***"); System.out.println("篮子中的苹果数量:" + basket.getAppleNumber() + "个"); Thread.sleep(1000); } } catch (InterruptedException e) {} } } ExecutorService service = Executors.newCachedThreadPool(); Producer producer = new Producer(); Consumer consumer = new Consumer(); service.submit(producer); service.submit(consumer); try { Thread.sleep(10000); } catch (InterruptedException e) {} service.shutdownNow(); } public static void main(String[] args) { BlockingQueueTest.testBasket(); } }
VieleJava-Schulungsvideos, alle auf der chinesischen PHP-Website, willkommen zum Online-Lernen!
Das obige ist der detaillierte Inhalt vonWas ist eine Java-Warteschlange?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!