Loading

Java阻塞队列

什么是阻塞队列

阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。
java中提供了七个阻塞队列,下面分别一一介绍。

ArrayBlockingQueue

介绍

一个数组实现的有界阻塞队列,此队列按照先进先出(FIFO)的原则对元素进行排序。支持公平锁和非公平锁。

特征

  • 基于数组实现,队列容量固定。
  • 先进先出,队列头是最先进队的元素,队列尾是最后进队的元素。
  • 存/取操作公用同一把锁(默认非公平锁ReentrantLock),无法实现真正意义上的存取并行。

分析

  • 构造方法

    由于ArrayBlockingQueue是有界队列,所以构造函数必须传入队列大小参数

    //接收一个整型的参数,这个整型参数指的是队列的长度
    public ArrayBlockingQueue(int capacity) {
        this(capacity, false);
    }
    
    //boolean类型的参数是作为可重入锁的参数进行初始化,规定可重入锁是公平锁还是非公平锁,默认false
    public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = new Object[capacity];
        lock = new ReentrantLock(fair);
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
    }
    
    //......
    public ArrayBlockingQueue(int capacity, boolean fair,
                              Collection<? extends E> c) {
        this(capacity, fair);
    
        final ReentrantLock lock = this.lock;
        lock.lock(); // Lock only for visibility, not mutual exclusion
        try {
            int i = 0;
            try {
                for (E e : c) {
                    checkNotNull(e);
                    items[i++] = e;
                }
            } catch (ArrayIndexOutOfBoundsException ex) {
                throw new IllegalArgumentException();
            }
            count = i;
            putIndex = (i == capacity) ? 0 : i;
        } finally {
            lock.unlock();
        }
    }
    
  • 通过下面的构造方法可以构建出ArrayBlockingQueue对象,构造方法中会实例化一些实例变量

    	//保存元素的数组
        final Object[] items;
    
    	//出队元素下标
        int takeIndex;
    
    	//入队元素下标
        int putIndex;
    
    	//统计队列元素个数
        int count;
    
    	//锁对象
        final ReentrantLock lock;
    
        //等待获取条件
        private final Condition notEmpty;
    
        //等待存放条件
        private final Condition notFull;
    
        /**
         * Shared state for currently active iterators, or null if there
         * are known not to be any.  Allows queue operations to update
         * iterator state.
         */
        transient Itrs itrs = null;
    

主要函数解析

add(E e)

  • add方法实际调用自己的 offer,参数不能为null。

  • 将 e 加到 ArrayBlockingQueue 当中,如果可以容纳返回true,否则抛出异常。

offer(E e)

  • 将 e 加到 ArrayBlockingQueue 当中,如果可以容纳返回true,否则返回false。

put(E e)

  • 将 e 加到 ArrayBlockingQueue 当中,如果没有空间,则调用此方法的线程会被阻断,直到有空间时继续。

poll()

  • 取走 ArrayBlockingQueue 排在首位的元素并返回。

poll(long timeout, TimeUnit unit)

  • 取走 ArrayBlockingQueue 排在首位的元素,若没有取到,则等待timeout规定的时间,取不到返回null。

take()

  • 取走 ArrayBlockingQueue 排在首位的元素,如果为空,阻断等待直到有新的元素加入。

remainingCapacity()

  • 返回队列剩余可用的大小。等于初始容量减去当前的size。

    注意 : 不能通过该方法来判断元素是否插入成功,因为有可能另外一个元素即将插入或删除一个元素。

LinkedBlockingQueue

介绍

一个由链表实现的有界阻塞队列,此队列按照先进先出(FIFO)的原则对元素进行排序。此队列的默认大小和长度为Integer.MAX_VALUE,所以默认创建的该队列有容量危险。

特征

  • 基于链表实现,默认容量大小 Integer.MAX_VALUE。
  • 存/取操作分别有独立的锁,可实现存/取并行执行。
  • 基于链表,新增和移除比数组快。但是每次新增/移除都会有Node对象的新增/移除,所以存在JVM GC的性能影响的可能。

分析

  • 构造方法

    //无参构造,默认长度为Integer.MAX_VALUE
    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }
    
    //接收一个整型的参数,这个整型参数指的是队列的长度    
    public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node<E>(null);
    }
    
    //接受一个集合类型的参数,长度默认为Integer.MAX_VALUE,集合元素不能为null
    public LinkedBlockingQueue(Collection<? extends E> c) {
        this(Integer.MAX_VALUE);
        final ReentrantLock putLock = this.putLock;
        putLock.lock(); // Never contended, but necessary for visibility
        try {
            int n = 0;
            for (E e : c) {
                if (e == null)
                    throw new NullPointerException();
                if (n == capacity)
                    throw new IllegalStateException("Queue full");
                enqueue(new Node<E>(e));
                ++n;
            }
            count.set(n);
        } finally {
            putLock.unlock();
        }
    }
    
  • 通过查看源码可以发现有两个Node,分别存储首、尾节点,并且可以看到还有一个初始值得原子变量,用来记录队列的元素个数。另外还有两个 ReentrantLock 用来控制元素入队和出队的原子性。如下

        //容量大小,默认Integer.MAX_VALUE
        private final int capacity;
    
        //元素个数,线程安全
        private final AtomicInteger count = new AtomicInteger();
    
        //头节点
        transient Node<E> head;
    
        //尾节点
        private transient Node<E> last;
    
        //执行take,poll时获取该锁
        private final ReentrantLock takeLock = new ReentrantLock();
    
        //队列为空,执行出队(如take)的线程会使用该条件进行等待
        private final Condition notEmpty = takeLock.newCondition();
    
        //执行 put, offer 获取该锁
        private final ReentrantLock putLock = new ReentrantLock();
    
        //队列满了,执行入队(如put)的线程会使用该条件进行等待
        private final Condition notFull = putLock.newCondition();
    

主要函数解析

put(E e)

  • e元素为null则抛出NullPointerException异常。
  • 在队列尾部插入一个元素,如果有空闲则插入完成返回,如果队列已满则阻塞当前线程,直到队列有空闲插入成功后返回。

offer(E e)

  • e元素为null则抛出NullPointerException异常。
  • 在队列尾部插入一个元素,如果队列有空闲则返回true,如果队列已满则丢弃当前元素然后返回false。
  • 该方法是阻塞的。

poll()

  • 从队列头部获取并移除一个元素,如果队列为空则返回null,该方法是不阻塞的。

poll(long timeout, TimeUnit unit)

  • 取走 ArrayBlockingQueue 排在首位的元素,若没有取到,则等待timeout规定的时间,取不到返回null。

peek()

  • 获取队列头部的元素,但不移除它。该方法是不阻塞的。

take()

  • 获取队列头部的元素并从队列中移除它。
  • 如果队列为空则阻塞当前线程直到队列不为空然后返回元素。

remove(Object o)

  • 删除队列中指定元素的元素,有则返回true,没有则返回false。

size()

  • 获取当前队列元素个数。

PriorityBlockingQueue

介绍

PriorityBlockingQueue是带优先级的无界阻塞队列,每次出队都返回优先级最高或最低的元素。

默认使用对象的CompareTo方法提供比较规则,若需要自定义比较规则可自定义Comparators。

特征

  • 基于数组实现,队列容量最大为 Integer.MAX_VALUE - 8【减8是因为数组对象头,为了避免内存溢出】
  • 优先队列不允许空值,而且不支持non-compareable(不可比较对象),比如用户自定义的类。

分析

  • 构造方法

    //创建一个默认初始容量为11的PriorityBlockingQueue,根据元素的自然顺序对其排序
    public PriorityBlockingQueue() {
        this(DEFAULT_INITIAL_CAPACITY, null);
    }
    
    //创建一个指定初始容量的PriorityBlockingQueue,根据元素的自然顺序对其排序
    public PriorityBlockingQueue(int initialCapacity) {
        this(initialCapacity, null);
    }
    
    //创建一个指定初始容量的PriorityBlockingQueue,根据指定的比较器对元素进行排列
    public PriorityBlockingQueue(int initialCapacity,Comparator<? super E> comparator) {
        //初始容量小于1会抛异常
        if (initialCapacity < 1)
            throw new IllegalArgumentException();
        //初始化锁
        this.lock = new ReentrantLock();
        //初始化条件变量
        this.notEmpty = lock.newCondition();
        //初始化比较器
        this.comparator = comparator;
        //初始化数组
        this.queue = new Object[initialCapacity];
    }
    
    //创建包含指定集合中的元素的PriorityBlockingQueue。
    //如果指定的集合是SortedSet或PriorityQueue,则该优先级队列将按照相同的顺序进行排序。
    //否则,该优先级队列将根据其元素的自然顺序进行排序。
    public PriorityBlockingQueue(Collection<? extends E> c) {
        this.lock = new ReentrantLock();
        this.notEmpty = lock.newCondition();
        boolean heapify = true; // true if not known to be in heap order
        boolean screen = true;  // true if must screen for nulls
        if (c instanceof SortedSet<?>) {
            SortedSet<? extends E> ss = (SortedSet<? extends E>) c;
            this.comparator = (Comparator<? super E>) ss.comparator();
            heapify = false;
        }
        else if (c instanceof PriorityBlockingQueue<?>) {
            PriorityBlockingQueue<? extends E> pq =
                (PriorityBlockingQueue<? extends E>) c;
            this.comparator = (Comparator<? super E>) pq.comparator();
            screen = false;
            if (pq.getClass() == PriorityBlockingQueue.class) // exact match
                heapify = false;
        }
        Object[] a = c.toArray();
        int n = a.length;
        // If c.toArray incorrectly doesn't return Object[], copy it.
        if (a.getClass() != Object[].class)
            a = Arrays.copyOf(a, n, Object[].class);
        if (screen && (n == 1 || this.comparator != null)) {
            for (int i = 0; i < n; ++i)
                if (a[i] == null)
                    throw new NullPointerException();
        }
        this.queue = a;
        this.size = n;
        if (heapify)
            heapify();
    }
    
  • 队列中的初始化属性

        //默认数组容量
        private static final int DEFAULT_INITIAL_CAPACITY = 11;
    
        //分配数组的最大大小
        private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    
        //存放队列元素
        private transient Object[] queue;
    
        //队列中元素数量
        private transient int size;
    
        //比较器,如果优先队列默认比较器,则为null
        private transient Comparator<? super E> comparator;
    
        //用于所有公共操作的锁
        private final ReentrantLock lock;
    
        //队列为空时的阻塞条件
        private final Condition notEmpty;
    
        //自旋锁,通过CAS获得
        private transient volatile int allocationSpinLock;
    
        //一个普通的PriorityQueue,仅用于序列化
        private PriorityQueue<E> q;
    

主要函数解析

offer(E e)

  • offer的作用是在队列中插入一个元素,由于是无界队列,所以会一直返回 true。

poll()

  • 获取队列内部堆树的根节点元素返回,并移除该元素。如果队列为空返回null。

put()

  • put 内部调用的是 offer()。将指定的元素插入这个优先级队列,由于该队列是无界的,这个方法永远不会阻塞。

take()

  • 获取队列内部堆树的根节点元素返回,并移除该元素。如果队列为空会阻塞,直到有线程调用 offer 方法添加成功后才会激活该线程。

size()

  • 返回队列中的元素数量。该方法在返回size前加了锁,以保证在调用size()时不会有其他线程入队和出队操作。另外由于size变量没有被Volatile修饰,所以加锁也保证了在多线程下size变量的内存可见性。

使用场景

VIP排队购票 : 用户购票的时候,根据不同的等级,优先放到队伍的前面,当存在票源的时候优先分配。

import java.util.concurrent.PriorityBlockingQueue;

/**
 * @author leizige
 */
class Ticket implements Comparable<Ticket> {
    private Integer level;
    private String ticketName;

    public Ticket(Integer level, String ticketName) {
        this.level = level;
        this.ticketName = ticketName;
    }

    @Override
    public int compareTo(Ticket o) {
        if (this.level > o.level) {
            return 1;
        }
        return -1;
    }

    @Override
    public String toString() {
        return this.level + "\t" + this.ticketName;
    }


}

/**
 * @author leizige
 */
public class PriorityBlockingQueueTest {
    public static void main(String[] args) throws InterruptedException {


        PriorityBlockingQueue<Ticket> ticketQueue = new PriorityBlockingQueue<>(11);
        ticketQueue.add(new Ticket(0, "level-0"));
        ticketQueue.add(new Ticket(4, "level-4"));
        ticketQueue.add(new Ticket(2, "level-2"));
        ticketQueue.add(new Ticket(1, "level-1"));
        ticketQueue.add(new Ticket(3, "level-3"));

        //TODO 返回该队列中元素的迭代器。迭代器不会以任何特定的顺序返回元素。
        // 返回的迭代器是弱一致的返回:一个遍历此队列中的元素的迭代器
//        Iterator<Ticket> iterator = ticketQueue.iterator();
//        while (iterator.hasNext()) {
//            System.err.println(iterator.next().toString());
//        }

        do {
            if (!ticketQueue.isEmpty())
                System.err.println(ticketQueue.take().toString());
        } while (!ticketQueue.isEmpty());

    }
}

DelayBlockingQueue

介绍

DelayQueue并发队列是一个无界阻塞延迟队列,队列中的每个元素都有个过期时间,当从队列获取元素时,只有过期元素才会出队列。队列头元素是最快要过期的元素。

特征

  • 不允许空元素。
  • 存储元素必须实现Delayed接口(Delayed接口继承了Comparable接口)。
  • 内部使用 PriorityQueue 存储数据。

分析

由于基于优先队列实现,但是它比较的是时间,可以根据需要来倒序或者正序排列。

  • 构造方法

    //初始化一个空的DelayQueue
    public DelayQueue() {}
    
    //创建一个DelayQueue,其中包含传入得集合元素
    public DelayQueue(Collection<? extends E> c) {
        this.addAll(c);
    }
    
  • 队列中的每个元素都要实现Delayed接口,由于每个元素都有一个过期时间,所以要实现获知当前元素剩下的过期时间的接口。由于队列内部使用优先队列来实现,所以要实现元素之间互相比较的接口。

    public interface Delayed extends Comparable<Delayed> {
    
        /**
         * Returns the remaining delay associated with this object, in the
         * given time unit.
         *
         * @param unit the time unit
         * @return the remaining delay; zero or negative values indicate
         * that the delay has already elapsed
         */
        long getDelay(TimeUnit unit);
    }
    

主要函数解析

offer(E e)

  • 插入元素到队列,如果元素为null则抛出NullPointerException异常,由于是无界队列,会一直返回true。插入的元素必须实现 Delayed接口。

take()

  • 获取并移除队列中延迟过期的元素,如果队列中没有过期元素则等待。

poll()

  • 获取并移除队头过期元素,如果没有过期元素返回null。

size()

  • 获取队列元素个数,包含过期和未过期的。

使用场景

  • 订单业务 :下单之后如果三十分钟之内没有付款就自动取消订单。
  • 短信通知 :下单成功六十秒后给用户发送短信通知。

简单实现

package queue;

import javax.validation.constraints.NotNull;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

/**
 * @author leizige
 */
public class Work implements Delayed {

    /**
     * 数据
     */
    private String msg;
    /**
     * 延迟时间
     */
    private Long delay;
    /**
     * 到期时间
     */
    private Long expire;
    /***
     * 创建时间
     */
    private Long now;


    private Work() {
    }

    public Work(String msg, Long delay) {
        this.msg = msg;
        this.delay = delay;
        /* 到期时间 = 当前时间 + 延迟时间 */
        expire = System.currentTimeMillis() + delay;
        now = System.currentTimeMillis();
    }

    /**
     * 需要实现的接口,获得延迟时间 过期时间 - 当前时间
     */
    @Override
    public long getDelay(@NotNull TimeUnit unit) {
        return unit.convert(this.expire - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
    }

    /**
     * 延迟队列内部排序,当前时间的延迟时间 - 比较对象的延迟时间
     */
    @Override
    public int compareTo(@NotNull Delayed o) {
        return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
    }

    @Override
    public String toString() {
        return "work{" + "delay=" + delay +
                ", expire=" + expire +
                ", msg='" + msg + '\'' +
                ", now=" + now +
                '}';
    }
}
package queue;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.DelayQueue;

/**
 * @author leizige
 */
public class DelayQueueTest {
    public static void main(String[] args) throws InterruptedException {


        BlockingQueue<Work> delayQueue = new DelayQueue<>();
        delayQueue.add(new Work("obj1", 1000L));
        delayQueue.add(new Work("obj3", 3000L));
        delayQueue.add(new Work("obj8", 8000L));
        delayQueue.add(new Work("obj2", 2000L));


        do {
            if (!delayQueue.isEmpty())
                System.err.println(delayQueue.take().toString());
        } while (!delayQueue.isEmpty());

    }
}

SynchronouseQueue

介绍

SynchronouseQueue 是一个不存储元素的阻塞队列。每一个put操作必须等待tack操作,否则不能添加元素。支持公平锁和非公平锁,默认情况下使用非公平性策略访问队列。

SynchronousQueue的一个使用场景是在线程池中,Executors.newCachedThreadPool()中就使用了SynchronousQueue,这个线程池根据需要(新任务到来时)创建新的线程,如果有空闲线程则会重复使用,线程空闲60s后会被回收。

分析

SynchronousQueue 可以看做一个传递者,负责把生产者线程处理的数据直接传递给消费者处理的线程,队列本身不存储任何元素,非常适合传递性的场景。并且 SynchronousQueue 的吞吐量高于 ArrayBlockingQueue 和 LinkedBlockingQueue。

  • 构造方法

    //创建不公平访问策略的SynchronousQueue
    public SynchronousQueue() {
        this(false);
    }
    
    //创建公平性访问的SynchronousQueue,如果fair==true,则等待的线程会以先进先出的顺序访问队列
    public SynchronousQueue(boolean fair) {
        transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
    }
    
  • SynchronousQueue 使用了内部类 Transfer 来转移数据,

    /**
    * Shared internal API for dual stacks and queues.
    */
    abstract static class Transferer<E> {
        /**
             * Performs a put or take.
             *
             * @param 如果e为null,则说明这是一个消费者线程,比如take操作
             *        如果e不为null,那么就是生产者线程,这个数据就是要交付的数据,比如put
             * @param timed if this operation should timeout
             * @param nanos the timeout, in nanoseconds
             * @return if non-null, the item provided or received; if null,
             *         the operation failed due to timeout or interrupt --
             *         the caller can distinguish which of these occurred
             *         by checking Thread.interrupted.
             */
        abstract E transfer(E e, boolean timed, long nanos);
    }
    

    SynchronousQueue 采用队列 TransferQueue 来实现公平性策略,采用堆栈 TransferStack 来实现非公平性策略。

  • TransferQueue ,TransferQueue 使用了 QNode 来作为队列节点

    /** Node class for TransferQueue. */
    static final class QNode {
        //队列中下一个节点
        volatile QNode next;          // next node in queue
        //数据项
        volatile Object item;         // CAS'ed to or from null
        //等待线程
        volatile Thread waiter;       // to control park/unpark
        //是否为数据标识
        final boolean isData;
    }
    

    TransferQueue 中主要有一下三个 QNode 对象

    //队列头
    transient volatile QNode head;
    //队列尾部
    transient volatile QNode tail;
    //指向一个已取消的节点,该节点可能未从队列中断开连接
    transient volatile QNode cleanMe;
    

    TransferQueue 构造方法

    TransferQueue() {
        QNode h = new QNode(null, false); // initialize to dummy node.
        head = h;
        tail = h;
    }
    
  • TransferStack ,TransferStack 使用了 SNode 来作为队列节点

    /** Node class for TransferStacks. */
    static final class SNode {
        //队列中下一个节点
        volatile SNode next;        // next node in stack
        //匹配的节点
        volatile SNode match;       // the node matched to this
        //等待线程
        volatile Thread waiter;     // to control park/unpark
        //数据项
        Object item;                // data; or null for REQUESTs
        int mode;
    }
    

    节点主要有以下三个状态

    //节点表示未满足的消费者
    static final int REQUEST    = 0;
    //节点表示未满足的生产者
    static final int DATA       = 1;
    //节点正在执行另一个未完成的数据或请求
    static final int FULFILLING = 2;
    
  • SynchronousQueue中的 put 、take都是依赖 TransferQueue或TransferTake的transfer方法实现的

    //将指定的元素添加到此队列中,并在必要时等待另一个线程接收它
    public void put(E e) throws InterruptedException {
        //不能为空
        if (e == null) throw new NullPointerException();
        //调用transfer方法
        if (transferer.transfer(e, false, 0) == null) {
            Thread.interrupted();
            throw new InterruptedException();
        }
    }
    
    public E take() throws InterruptedException {
        //调用transfer方法
        E e = transferer.transfer(null, false, 0);
        if (e != null)
            return e;
        Thread.interrupted();
        throw new InterruptedException();
    }
    

使用场景

插入数据的线程和获取数据的线程,交替执行

package queue;

import java.util.UUID;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;

/**
 * @author leizige
 */
public class SynchronousQueueExample {


    static class SynchronousQueueProducer implements Runnable {

        private BlockingQueue<String> blockingQueue;

        public SynchronousQueueProducer(BlockingQueue<String> blockingQueue) {
            this.blockingQueue = blockingQueue;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    String data = UUID.randomUUID().toString();
                    System.out.println("Put:" + data);
                    blockingQueue.put(data);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    static class SynchronousQueueConsumer implements Runnable {
        private BlockingQueue<String> blockingQueue;

        public SynchronousQueueConsumer(BlockingQueue<String> blockingQueue) {
            this.blockingQueue = blockingQueue;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    String data = blockingQueue.take();
                    System.out.println(Thread.currentThread().getName()
                            + " take(): " + data);
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    public static void main(String[] args) {
        final BlockingQueue<String> blockingQueue = new SynchronousQueue<>();

        SynchronousQueueProducer queueProducer = new SynchronousQueueProducer(blockingQueue);
        new Thread(queueProducer).start();

        SynchronousQueueConsumer queueConsumer_1 = new SynchronousQueueConsumer(blockingQueue);
        new Thread(queueConsumer_1).start();

        SynchronousQueueConsumer queueConsumer_2 = new SynchronousQueueConsumer(blockingQueue);
        new Thread(queueConsumer_2).start();

    }

}

应用场景

Executors.newCachedThreadPool()

    /**
     * Creates a thread pool that creates new threads as needed, but
     * will reuse previously constructed threads when they are
     * available.  These pools will typically improve the performance
     * of programs that execute many short-lived asynchronous tasks.
     * Calls to {@code execute} will reuse previously constructed
     * threads if available. If no existing thread is available, a new
     * thread will be created and added to the pool. Threads that have
     * not been used for sixty seconds are terminated and removed from
     * the cache. Thus, a pool that remains idle for long enough will
     * not consume any resources. Note that pools with similar
     * properties but different details (for example, timeout parameters)
     * may be created using {@link ThreadPoolExecutor} constructors.
     *
     * @return the newly created thread pool
     */
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

LinkedTransferQueue

介绍

一个由链表结构组成的无界阻塞队列。相对于其他队列,LinkedTransferQueue队列多了transfer和tryTransfer方法。

......

LinkedBlockingDeque

介绍

一个由链表结构组成的双向阻塞队列。队列头部和尾部可以添加和移除元素,多线程并发时,可以将锁的竞争最多降到一半。

......

posted @ 2021-04-29 15:42  不颓废青年  阅读(112)  评论(0编辑  收藏  举报