On blocking queue

Posted by Bret on Sat, 16 Oct 2021 08:51:14 +0200

1. What is a blocking queue?

  1. The first is a queue. The second is blocking. In the producer consumer model, the producer produces while the consumer consumes, so imagine these two threads as two threads, one is plugging in and the other is taking out.
    1. When the production thread is full and the consumption thread is not taken out in time, it is blocked.
    2. When the consumer thread consumes too fast, the producer has not had time to produce, resulting in nothing to take and blocking.
  2. Benefits of blocking queues: you don't need to care when to block threads and wake up threads.

2. Interface and its implementation class

  1. ArrayBlockQueue: a bounded blocking queue composed of an array structure.
  2. LinkedBlockingQueue: a bounded (but the default size is Integer.MAX_VALUE) blocking queue composed of a linked list structure. Although bounded, it is equivalent to unbounded.
  3. Synchronous queue: a blocking queue that does not store elements, that is, a queue of individual elements.
  4. LinkedBlockingDeque: a bidirectional blocking queue composed of a linked list structure.
  5. LinkedTransferQueue: an unbounded blocking queue composed of a linked list structure.
  6. DelayQueue: delay unbounded blocking queue implemented using priority queue.
  7. PriorityBlockQueue: an unbounded blocking queue that supports prioritization.
    Note: really master the first three.

3. Core method

Throw exceptionWhen the blocking queue is full: IIIegalStateException: Queue full will be thrown when add ing an element to the queue. When the blocking queue is empty: NoSuchException will be thrown when removing an element from the queue. remove() will be used according to the first in first out principle. If remove(Object o) is used, the specific element will be deleted
specific characteristicsInsert method, success true, failure false, remove method: successfully return the queue element, and return empty if the queue is not
Always blockedWhen the blocking queue is full, the producer continues to put elements into the queue, and the queue will block the production thread until the put data or response interrupt exits. When the blocking queue is empty, the consumer thread attempts to take elements from the queue, and the queue will block the consumer thread until the queue is available.
Timeout exitWhen the blocking queue is full, the producer thread in the queue will be blocked for a certain time. After the time limit is exceeded, the producer thread will exit

4. Producers and consumers

  1. Tradition:
    class ShareData {
    
        private int number = 0;
    
        private Lock lock = new ReentrantLock();
    
        private Condition condition = lock.newCondition();
    
        public void increment() throws Exception{
    
            // Sync code block, lock
            lock.lock();
            try {
                // Judge that while must be used here. If if if is used, there will be a false wake-up problem
                while(number != 0) {
                    // Waiting cannot produce
                    condition.await();
                }
    
                // work
                number++;
    
                System.out.println(Thread.currentThread().getName() + "\t " + number);
    
                // Notification wake up
                condition.signalAll();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void decrement() throws Exception{
            // Sync code block, lock
            lock.lock();
            try {
                // judge
                while(number == 0) {
                    // Waiting cannot be consumed
                    condition.await();
                }
    
                // work
                number--;
    
                System.out.println(Thread.currentThread().getName() + "\t " + number);
    
                // Notification wake up
                condition.signalAll();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
    }
    public class ProdConsumerTraditionDemo {
    
        public static void main(String[] args) {
    
            // High cohesion and low coupling cohesion refer to an air conditioner with its own method to adjust the temperature
    
            ShareData shareData = new ShareData();
    
            // t1 thread, production
            new Thread(() -> {
                for (int i = 0; i < 5; i++) {
                    try {
                        shareData.increment();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }, "t1").start();
    
            // t2 thread, consumption
            new Thread(() -> {
                for (int i = 0; i < 5; i++) {
                    try {
                        shareData.decrement();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }, "t2").start();
        }
    }
    
    Problem: too many details need to be controlled.
  2. Producers and consumers using blocking queues:
    Knowledge points: volatile, CAS, atomic class, blocking queue, thread interaction, atomic reference
    class MyResource {
        // It is enabled by default for production and consumption
        // volatile is used here to maintain the visibility of data, that is, when FLAG is modified, other threads should be notified to modify it immediately
        private volatile boolean FLAG = true;
    
        // Use atomic wrapper classes instead of number++
        private AtomicInteger atomicInteger = new AtomicInteger();
    
        // You cannot instantiate a specific SynchronousBlockingQueue to satisfy the condition
        BlockingQueue<String> blockingQueue = null;
    
        // Instead, you should use the structure injection method in dependency injection to pass in
        public MyResource(BlockingQueue<String> blockingQueue) {
            this.blockingQueue = blockingQueue;
            // Find out what the incoming class is
            System.out.println(blockingQueue.getClass().getName());
        }
    
        /**
         * production
         * @throws Exception
         */
        public void myProd() throws Exception{
            String data = null;
            boolean retValue;
            // When judging a multithreaded environment, you must use while to prevent false wakeups
            // When FLAG is true, start production
            while(FLAG) {
                data = atomicInteger.incrementAndGet() + "";
    
                // Store 1 data in 2 seconds
                retValue = blockingQueue.offer(data, 2L, TimeUnit.SECONDS);
                if(retValue) {
                    System.out.println(Thread.currentThread().getName() + "\t Insert queue:" + data  + "success" );
                } else {
                    System.out.println(Thread.currentThread().getName() + "\t Insert queue:" + data  + "fail" );
                }
    
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
            System.out.println(Thread.currentThread().getName() + "\t Stop production, indicating FLAG=false,Production introduction");
        }
    
        /**
         * consumption
         * @throws Exception
         */
        public void myConsumer() throws Exception{
            String retValue;
            // When judging a multithreaded environment, you must use while to prevent false wakeups
            // When FLAG is true, start production
            while(FLAG) {
                // Store 1 data in 2 seconds
                retValue = blockingQueue.poll(2L, TimeUnit.SECONDS);
                if(retValue != null && retValue != "") {
                    System.out.println(Thread.currentThread().getName() + "\t Consumption queue:" + retValue  + "success" );
                } else {
                    FLAG = false;
                    System.out.println(Thread.currentThread().getName() + "\t Consumption failed. The queue is empty. Exit" );
    
                    // Exit the consumption queue
                    return;
                }
            }
        }
    
        /**
         * Judgment of stopping production
         */
        public void stop() {
            this.FLAG = false;
        }
    
    }
    public class ProdConsumerBlockingQueueDemo {
    
        public static void main(String[] args) {
            // Pass in the specific implementation class, ArrayBlockingQueue
            MyResource myResource = new MyResource(new ArrayBlockingQueue<String>(10));
    
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "\t Production thread start");
                System.out.println("");
                System.out.println("");
                try {
                    myResource.myProd();
                    System.out.println("");
                    System.out.println("");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }, "prod").start();
    
    
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "\t Consumer thread start");
    
                try {
                    myResource.myConsumer();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }, "consumer").start();
    
            // After 5 seconds, stop production and consumption
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            System.out.println("");
            System.out.println("");
            System.out.println("5 Seconds later, the production and consumption threads stop and the thread ends");
            myResource.stop();
        }
    }
    

Topics: Java RabbitMQ data structure