Blocking queue for concurrent programming in Java

Blocking queue for concurrent programming in Java

There are 2 ways to implement thread-safe teams:

  1. Blocking, that is, locking
  2. Non-blocking, using CAS, ConcurrentLinkedQueue is used in this way

Blocking queues provide two additional operations, blocking add and blocking remove:

  • Blocking Add: When the queue is full, the queue blocks the thread that adds elements until the queue is full.
  • Blocking removal: When the queue is empty, the queue blocks the thread that removes elements until the queue is not empty.
operation/processing method Throw an exception return special value keep blocking time out
Enqueue method add(e) offer(e) put(e) offer(e, time, unit)
Dequeue method remove() poll() take() poll(time, unit)
Inspection Method element() peek()

The return value type of the offer() method is boolean, and it returns false if the enqueue fails;
the poll() method returns null if the dequeue fails; the
peek() method only obtains the element, but does not execute the dequeue, and returns when no element is obtained. null;

Usage scenarios: Producer/Consumer, Producer adds elements to the queue, Consumers get elements from the queue.

This article introduces 7 blocking queues in Java.

ArrayBlockingQueue

The bottom layer of ArrayBlockingQueue is data, which is a bounded blocking queue, which sorts elements according to the FIFO principle.

public class TestArrayBlockingQueue {

    // 创建一个容量为5的ArrayBlockingQueue
    static BlockingQueue<String> queue = new ArrayBlockingQueue<String>(5);
    
    public static void main(String[] args) throws Exception {
        // 启动10个生产者线程, 每个生产者向queue中put自己的线程名称
        // 并且put之后打印自己的名称
        for(int i=0; i<10; i++) {
            new Thread(new Runnable(){
                public void run() {
                    try {
                        queue.put(Thread.currentThread().getName());
                        System.out.println(Thread.currentThread().getName() + " put");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "producer-"+i).start();
        }
        Thread.sleep(5000); // 睡5秒, 让10个生产者都开始执行
        // 打印队列的实际长度, 为5, 
        // 因为没有被消费, 所以只有5个生产者线程put成功, 
        // 其他线程被阻塞
        System.out.println("queue size: " + queue.size());
        // 创建10个消费者线程, 每个线程去queue中做一次take操作
        // 每take一个元素, 就会有一个生产者线程被唤醒, 向queue中put元素
        for(int i=0; i<10; i++) {
            new Thread(new Runnable(){
                public void run() {
                    try {
                        queue.take();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "concumer-"+i).start();
        }
        Thread.sleep(5000);
        System.out.println("queue size: " + queue.size());
    }
}

ArrayBlockingQueue is an unfair queue by default; a
fair queue refers to a blocked thread, which can access the queue in the order in which it is blocked, that is, the thread that is blocked first accesses the queue first. An unfair queue refers to a blocked queue, when the queue is available, both Have equal qualifications to compete for resources. To create a fair queue, you can use another constructor of ArrayBlockingQueue:

BlockingQueue<String> queue = new ArrayBlockingQueue<String>(5, true);

LinkedBlockingQueue

The underlying implementation of LinkedBlockingQueue is a linked list, similar to ArrayBlockingQueue, it is a bounded blocking queue; the difference is that LinkedBlockingQueue can not specify the queue length, in this case the length is Integer.MAX_VALUE; LinkedBlockingQueue also sorts elements according to the FIFO principle.

PriorityBlockingQueue

PriorityBlockingQueue can be seen from the name, it is a blocking queue that supports priority sorting.
The underlying implementation of PriorityBlockingQueue is data, which is an unbounded blocking queue. It does not block the producer thread, only the consumer thread , so when the producer Produced faster than consumers can consume all available memory over time.

PriorityBlockingQueue supports sorting by custom compareTo()methods, or sorting by specification Comparator.

public class TestPriorityBlockingQueue {

    static BlockingQueue<RegisterInfo> queue = new PriorityBlockingQueue<RegisterInfo>();
    
    private static class RegisterInfo implements Comparable<RegisterInfo> {
        
        private String name;
        
        private int priority;
        
        public RegisterInfo(String name, int priority) {
            this.name = name;
            this.priority = priority;
        }

        @Override
        public int compareTo(RegisterInfo registerInfo) {
            if(this.priority > registerInfo.getPriority()) {
                return 1;
            } else if (this.priority < registerInfo.getPriority()){
                return -1;
            } else {
                return 0;
            }
        }

        public String getName() {
            return name;
        }
        
        public int getPriority() {
            return priority;
        }
        
    }
    
    public static void main(String[] args) throws Exception {
        RegisterInfo info1 = new RegisterInfo("张三", 5);
        RegisterInfo info2 = new RegisterInfo("李四", 7);
        RegisterInfo info3 = new RegisterInfo("王五", 3);
        queue.add(info1);
        queue.add(info2);
        queue.add(info3);
        System.out.println(queue.take().getName());
        System.out.println(queue.take().getName());
        System.out.println(queue.take().getName());
    }
}

DelayQueue

DelayQueue is an unbounded queue that supports delayed acquisition of elements. It is implemented using PriorityQueue internally. Elements placed in the DelayQueue queue must implement the Delayed interface. When creating an element, specify how long to delay before getting this element from the queue.

DelayQueue can be used for cache design, use DelayQueue to save the effective time of the cache, use a thread circular queue, when the element can be taken out from the queue, it means that the effective time of the cache is up.

public class TestDelayQueue {

    static BlockingQueue<CacheTime> queue = new DelayQueue<CacheTime>();
    
    private static class CacheTime implements Delayed {
        
        private String id;
        
        private long time;
        
        public CacheTime(String id, long time) {
            this.id = id;
            this.time = time;
        }

        @Override
        public int compareTo(Delayed delayed) {
            long diff = 0L;
            if(delayed instanceof CacheTime) {
                CacheTime other = (CacheTime) delayed;
                diff = this.time - other.time;
            } else {
                diff = this.time - delayed.getDelay(TimeUnit.MILLISECONDS);
            }
            if(diff > 0) {
                return 1;
            } else if(diff < 0) {
                return -1;
            } else {
                return 0;
            }
        }

        @Override
        public long getDelay(TimeUnit unit) {
            return time - System.currentTimeMillis();
        }
        
        public String getId() {
            return this.id;
        }
    }
    
    public static void main(String[] args) throws Exception {
        long now = System.currentTimeMillis();
        CacheTime ct1 = new CacheTime("001", now + 3000);
        CacheTime ct2 = new CacheTime("002", now + 5000);
        CacheTime ct3 = new CacheTime("003", now + 1000);
        queue.add(ct1);
        queue.add(ct2);
        queue.add(ct3);
        for(int i=0; i<3; i++) {
            System.out.println(queue.take().getId());
        }
    }
}

The CacheTime class is used to simulate the effective time of the cache, id is the cache id, and time is the effective time of the cache.
CacheTime implements the Delayed interface: the
getDelay()method is used to return the remaining delay time of this element The
compareTo()method is used for sorting

In the main() method, 3 CacheTime objects are created, and the delay time is set to 3 seconds, 5 seconds, and 1 second; add them to the DelayQueue, cycle the DelayQueue, and the elements will be changed by the delay time according to the delay time. Delayed out of the queue in order from less to more.

SynchronousQueue

SynchronousQueue is a blocking queue with no elements, and its capacity is 0. Each time a put operation is performed, a take operation must wait.
SynchronousQueue supports fair locks. TransferQueue and TransferStack are used internally to store references to blocked threads.

The TransferQueue here is the inner class of SynchronousQueue, not the TransferQueue interface that inherits BlockingQueue.

public class TestSynchronousQueue {

    static BlockingQueue<String> queue = new SynchronousQueue<String>();
    
    public static void main(String[] args) throws Exception {
        new Thread(new Runnable(){
            @Override
            public void run() {
                try {
                    queue.put("hello");
                    System.out.println("put complete...");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "producer").start();
        Thread.sleep(1000);
        System.out.println("queue size: " + queue.size());
        new Thread(new Runnable(){
            @Override
            public void run() {
                try {
                    System.out.println("take: " + queue.take());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "consumer").start();
    }
}

In the example, the producer thread will block until the consumer thread executes the take operation.

One usage scenario of SynchronousQueue is in the thread pool. Executors.newCachedThreadPool()SynchronousQueue is used internally.

LinkedTransferQueue

LinkedTransferQueue is an unbounded blocking queue implemented with a linked list.
LinkedTransferQueue implements the TransferQueue interface and extends the transfer and tryTransfer methods.

  • transfer(): When the producer thread adds an element to the queue, if there is a consumer thread waiting for consumption, the element is directly transferred to the producer thread; if there is no consumer waiting for consumption, the element is stored in the queue and waits until the consumer Returns after thread consumption.
  • tryTransfer(): try to transfer the element to the consumer thread, if there is a consumer thread waiting to receive the element, return true, otherwise return false. That is, the tryTransfer() method will return immediately regardless of whether there is a consumer thread waiting to receive the element. If it is a tryTransfer(E e, long timeout, TimeUnit unit)method , if there is no consumer thread, wait for the specified time before returning.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324978497&siteId=291194637