Heim  >  Artikel  >  Java  >  Analysieren Sie Beispiele für blockierende Warteschlangen in Java

Analysieren Sie Beispiele für blockierende Warteschlangen in Java

PHPz
PHPznach vorne
2023-05-09 21:43:061333Durchsuche

    1. Was ist eine Blockierungswarteschlange?

    Eine Blockierungswarteschlange ist eine spezielle Warteschlange in der Datenstruktur. Gleichzeitig folgt sie dem First-In-First-Out-Prinzip. Die Blockierungswarteschlange ist eine threadsichere Warteschlangendatenstruktur und weist die folgenden zwei Merkmale auf: Wenn die Warteschlange voll ist, führt das weitere Einfügen von Elementen in die Warteschlange dazu, dass die Warteschlange blockiert, bis andere Threads Elemente aus der Warteschlange übernehmen Wenn die Warteschlange leer ist, wird auch weiterhin aus der Warteschlange entfernt Die Warteschlange wird blockiert, bis andere Threads Elemente in die Warteschlange einfügen

    Zusätzlich: Thread-Blockierung bedeutet, dass der Code zu diesem Zeitpunkt nicht ausgeführt wird, dh das Betriebssystem plant dies nicht Thread an die CPU zur Ausführung zu diesem Zeitpunkt senden

    2. Verwenden Sie den blockierenden Warteschlangencode

    import java.util.concurrent.LinkedBlockingDeque;
    import java.util.concurrent.BlockingDeque;
    public class Test {
        public static void main(String[] args) throws InterruptedException {
            //不能直接newBlockingDeque,因为它是一个接口,要向上转型
            //LinkedBlockingDeque内部是基于链表方式来实现的
            BlockingDeque<String> queue=new LinkedBlockingDeque<>(10);//此处可以指定一个具体的数字,这里的的10代表队列的最大容量
            queue.put("hello");
            String elem=queue.take();
            System.out.println(elem);
            elem=queue.take();
            System.out.println(elem);
        }
    }

    Hinweis: Die Put-Methode hat eine Blockierungsfunktion, das Angebot jedoch nicht, daher wird im Allgemeinen die Put-Methode verwendet (der Grund, warum die Angebotsmethode verwendet wird). verwendet werden kann, ist, dass BlockingDeque Queue erbt)BlockingDeque继承了Queue

    Analysieren Sie Beispiele für blockierende Warteschlangen in Java


    打印结果如上所示,当打印了hello后,队列为空,代码执行到elem=queue.take();

    Beispielanalyse einer Blockierungswarteschlange in Java

    Das Druckergebnis ist wie oben gezeigt. Nach dem Drucken von Hallo ist die Warteschlange leer und der Code wird erst dann weiter ausgeführt, wenn elem= ist queue.take(); Zu diesem Zeitpunkt wechselt der Thread in den Wartezustand „Blockieren“. Es wird nichts gedruckt, bis andere Threads neue Elemente in die Warteschlange stellen

    3 wird zwischen Serverentwicklung und Back-End-Entwicklung verglichen. Häufig verwendete Programmiermethoden werden im Allgemeinen zum Entkoppeln, Peak Shaving und Valley Filling verwendet.

    Hohe Kopplung: Die Korrelation zwischen zwei Codemodulen ist relativ hoch.

    Hohe Kohäsion: Jedes Element in einem Codemodul ist eng miteinander integriert.

    Daher streben wir im Allgemeinen eine hohe Kohäsion und eine geringe Kopplung an, was die Ausführungseffizienz beschleunigt Verwenden Sie das Producer-Consumer-Modell wird direkt übertragen, dann sendet entweder A Daten an B oder B ruft Daten von A ab. Beide erfordern eine direkte Interaktion zwischen A und B, sodass A und B eine Abhängigkeitsbeziehung haben (der Kopplungsgrad von A und B ist relativ hoch). . Wenn der Server in Zukunft erweitert werden muss, beispielsweise durch Hinzufügen eines C-Servers, damit A Daten an C übertragen kann, werden die Änderungen komplizierter und die Effizienz verringert. Zu diesem Zeitpunkt können wir eine Warteschlange hinzufügen, bei der es sich um eine blockierende Warteschlange handelt. Wenn A Daten in die Warteschlange schreibt und B sie daraus entnimmt, entspricht die Warteschlange einer Transferstation (oder einem Handelsplatz) und A entspricht Ein Produzent (der Daten bereitstellt). B entspricht dem Verbraucher (der Daten empfängt).

    Analysieren Sie Beispiele für blockierende Warteschlangen in JavaIn einem Computer fungieren Produzenten als eine Gruppe von Threads und Verbraucher als eine andere Gruppe von Threads, und Handelsplätze können Blockierungswarteschlangen verwenden

    (2) Anwendung 2: Peak Shaving und Valley Filling

    Analysieren Sie Beispiele für blockierende Warteschlangen in JavaIm wirklichen Leben ist der Damm ein sehr wichtiger Teil des Flusses. Wenn es keinen Damm gibt, stellen Sie sich das Ergebnis vor: Wenn die Hochwassersaison kommt und das Wasser flussaufwärts sehr groß ist, fließt viel Wasser Überschwemmungen führen dazu, dass die Ernte überschwemmt wird. In Dürreperioden gibt es flussabwärts nur wenig Wasser, was zu Dürren führen kann. Wenn ein Damm vorhanden ist, speichert der Damm während der Hochwassersaison überschüssiges Wasser im Damm, schließt das Tor, um Wasser zu speichern, und lässt das stromaufwärts gelegene Wasser mit einer bestimmten Geschwindigkeit flussabwärts fließen, um zu verhindern, dass eine plötzliche Welle starken Regens den Damm überschwemmt flussabwärts, damit es nicht zu Überschwemmungen kommt. In Dürreperioden gibt der Damm zuvor gespeichertes Wasser ab und lässt das Wasser mit einer bestimmten Geschwindigkeit flussabwärts fließen, um zu verhindern, dass es flussabwärts zu Wassermangel kommt. Dadurch können sowohl Überschwemmungen während der Hochwassersaison als auch Dürren während der Trockenperiode vermieden werden.

    Peak: entspricht der Hochwassersaison

    Tal: entspricht der Trockenzeit
    Bei Computern
    Diese Situation ist auch bei Computern sehr typisch, insbesondere bei der Serverentwicklung. Das Gateway leitet Anfragen aus dem Internet normalerweise an Unternehmensserver weiter, z. B. an einige Produkte Server, Benutzerserver, Händlerserver (speichert Händlerinformationen), Live-Broadcast-Server. Da die Anzahl der aus dem Internet kommenden Anfragen jedoch nicht kontrollierbar ist, entspricht dies dem Upstream-Wasser. Wenn plötzlich eine große Welle von Anfragen eintrifft, brechen viele nachfolgende Server zusammen, nachdem sie viele Anfragen verarbeitet haben Die Anforderung umfasst eine Reihe von Datenbankvorgängen, da die Effizienz datenbankbezogener Vorgänge relativ gering ist. Wenn zu viele Anforderungen vorhanden sind, kann sie nicht verarbeitet werden, sodass es zum Absturz kommt

    所以实际情况中网关和业务服务器之间往往用一个队列来缓冲,这个队列就是阻塞队列(交易场所),用这个队列来实现生产者(网关)消费者(业务服务器)模型,把请求缓存到队列中,后面的消费者(业务服务器)按照自己固定的速率去读请求。这样当请求很多时,虽然队列服务器可能会稍微受到一定压力,但能保证业务服务器的安全。

    (3)相关代码

    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.LinkedBlockingQueue;
    
    public class TestDemo {
        public static void main(String[] args) {
            // 使用一个 BlockingQueue 作为交易场所
            BlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
            // 此线程作为消费者
            Thread customer = new Thread() {
                @Override
                public void run() {
                    while (true) {
                        // 取队首元素
                        try {
                            Integer value = queue.take();
                            System.out.println("消费元素: " + value);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
            customer.start();
            // 此线程作为生产者
            Thread producer = new Thread() {
                @Override
                public void run() {
                    for (int i = 1; i <= 10000; i++) {
                        System.out.println("生产了元素: " + i);
                        try {
                            queue.put(i);
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
            producer.start();
            try {
                customer.join();
                producer.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    Analysieren Sie Beispiele für blockierende Warteschlangen in Java

    打印如上(此代码是让生产者通过sleep每过1秒生产一个元素,而消费者不使用sleep,所以每当生产一个元素时,消费者都会立马消费一个元素)

    4.阻塞队列和生产者消费者模型功能的实现

    在学会如何使用BlockingQueue后,那么如何自己去实现一个呢?
    主要思路:

    • 1.利用数组

    • 2.head代表队头,tail代表队尾

    • 3.head和tail重合后到底是空的还是满的判断方法:专门定义一个size记录当前队列元素个数,入队列时size加1出队列时size减1,当size为0表示空,为数组最大长度就是满的(也可以浪费一个数组空间用head和tail重合表示空,用tail+1和head重合表示满,但此方法较为麻烦,上一个方法较为直观,因此我们使用上一个方法)

    public class Test2 {
        static class BlockingQueue {
        private int[] items = new int[1000];    // 此处的1000相当于队列的最大容量, 此处暂时不考虑扩容的问题.
        private int head = 0;//定义队头
        private int tail = 0;//定义队尾
        private int size = 0;//数组大小
        private Object locker = new Object();
    
        // put 用来入队列
        public void put(int item) throws InterruptedException {
            synchronized (locker) {
                while (size == items.length) {
                    // 队列已经满了,阻塞队列开始阻塞
                    locker.wait();
                }
                items[tail] = item;
                tail++;
                // 如果到达末尾, 就回到起始位置.
                if (tail >= items.length) {
                    tail = 0;
                }
                size++;
                locker.notify();
            }
        }
        // take 用来出队列
        public int take() throws InterruptedException {
            int ret = 0;
            synchronized (locker) {
                while (size == 0) {
                    // 对于阻塞队列来说, 如果队列为空, 再尝试取元素, 就要阻塞
                    locker.wait();
                }
                ret = items[head];
                head++;
                if (head >= items.length) {
                    head = 0;
                }
                size--;
                // 此处的notify 用来唤醒 put 中的 wait
                locker.notify();
            }
            return ret;
        }
    }
    
        public static void main(String[] args) throws InterruptedException {
            BlockingQueue queue = new BlockingQueue();
            // 消费者线程
            Thread consumer = new Thread() {
                @Override
                public void run() {
                    while (true) {
                        try {
                            int elem = queue.take();
                            System.out.println("消费元素: " + elem);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
            consumer.start();
    
            // 生产者线程
            Thread producer = new Thread() {
                @Override
                public void run() {
                    for (int i = 1; i < 10000; i++) {
                        System.out.println("生产元素: " + i);
                        try {
                            queue.put(i);
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
            producer.start();
            consumer.join();
            producer.join();
        }
    }

    Analysieren Sie Beispiele für blockierende Warteschlangen in Java

    运行结果如上。
    注意:

    • 1.wait和notify的正确使用

    • 2.put和take都会产生阻塞情况,但阻塞条件是对立的,wait不会同时触发(put唤醒take阻塞,take唤醒put阻塞)

    Das obige ist der detaillierte Inhalt vonAnalysieren Sie Beispiele für blockierende Warteschlangen in Java. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

    Stellungnahme:
    Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen