并发容器之队列

线程安全的队列分为阻塞队列和非阻塞队列。

非阻塞队列ConcurrentLinkedQueue

使用 CAS 非阻塞算法来实现线程安全,无界非阻塞队列
属性

    transient volatile Node<E> head;
    private transient volatile Node<E> tail;

构造方法

 public ConcurrentLinkedQueue() {
        head = tail = new Node<E>();//哨兵
    }

加入元素

 public boolean offer(E e) {
        final Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
        //从尾部插入节点
        for (Node<E> t = tail, p = t;;) {
            Node<E> q = p.next;
            // p是最后一个节点
            if (q == null) {
                //使用CAS插入节点         
                if (NEXT.compareAndSet(p, null, newNode)) {                  
                    if (p != t) // hop two nodes at a time; failure is OK
                        TAIL.weakCompareAndSet(this, t, newNode);
                    return true;
                }
                // Lost CAS race to another thread; re-read next
            }
            else if (p == q)
                // We have fallen off list.  If tail is unchanged, it
                // will also be off-list, in which case we need to
                // jump to head, from which all live nodes are always
                // reachable.  Else the new tail is a better bet.
                p = (t != (t = tail)) ? t : head;
            else
                // Check for tail updates after two hops.
                p = (p != t && t != (t = tail)) ? t : q;
        }
    }

阻塞队列BlockingQueue

提供了可阻塞的插入和移除方法。当队列满时,执行插入操作的线程会被阻塞;当队里空时,执行移除方法的线程被阻塞。阻塞队列(BlockingQueue)被广泛使用在“生产者-消费者”问题中。

常用方法

抛出异常返回值阻塞超时
插入add(e)offer(e)put(e)offer(e,time,unit)
移除remove(e)poll ()take()poll(time,unit)
检查element(e)peek()

抛出异常:如果操作无法立即执行,抛异常。
返回值:如果操作无法立即执行,返回一个特定的值( true / false)。
阻塞:如果操作无法立即执行,该方法调用将会发生阻塞,直到能够执行。
超时:如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行,但等待时间不会超过给定值。

LinkedBlockingQueue

可以通过构造函数设置是否有界,使用ReentrantLock,Condition和Atomic实现

数据结构

 static class Node<E> {
        E item;   
        Node<E> next;
        Node(E x) { item = x; }
    }

属性
读取分别有一把锁,可以同时进行

    //容量
    private final int capacity;
   //元素数量
    private final AtomicInteger count = new AtomicInteger();
    //哨兵
    transient Node<E> head;

     /** Lock held by take, poll, etc */
    private final ReentrantLock takeLock = new ReentrantLock();

    /** Wait queue for waiting takes */
    private final Condition notEmpty = takeLock.newCondition();

    /** Lock held by put, offer, etc */
    private final ReentrantLock putLock = new ReentrantLock();

    /** Wait queue for waiting puts */
    private final Condition notFull = putLock.newCondition();

构造方法

    //无参,容量为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);
    }

加入元素
add()方法内部调用offer(),offer返回boolean类型数据,add会抛出异常
add()是AbstractQueue提供的方法
LinkedBlockingQueue提供了put和offer方法
offer()在队列满时会直接返回false,在没有获取到锁时会一直等待
put()在队列满时会加入等待队列,没有获取到锁时可以被中断等待

  public boolean offer(E e) {
        if (e == null) throw new NullPointerException();
        final AtomicInteger count = this.count;
        //如果队列已满  返回false
        if (count.get() == capacity)
            return false;
        final int c;
        final Node<E> node = new Node<E>(e);
        final ReentrantLock putLock = this.putLock;
        //加锁,待获取锁成功后,才响应中断。
        putLock.lock();
        try {
            //如果队列已满  返回false
            if (count.get() == capacity)
                return false;
            //入队
            enqueue(node);
            //count原子自增
            c = count.getAndIncrement();
            if (c + 1 < capacity)
               //唤醒condition队列中阻塞put()的线程
                notFull.signal();
        } finally {
            putLock.unlock();
        }
        if (c == 0)
            signalNotEmpty();//唤醒阻塞在take()的线程
        return true;
    }
 public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        final int c;
        final Node<E> node = new Node<E>(e);
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        //加锁,允许在等待时由其他线程的Thread.interrupt()方法来中断等待
        putLock.lockInterruptibly();
        try {        
            while (count.get() == capacity) {
                notFull.await();//队列已满,加入等待队列
            }
            enqueue(node);
            c = count.getAndIncrement();
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            putLock.unlock();
        }
        if (c == 0)
            signalNotEmpty();
    }

ArrayBlockingQueue:有界队列

属性
读取共用同一把锁,不能同时进行。

也许是因为ArrayBlockingQueue的数据写入和获取操作已经足够轻巧,以至于引入独立的锁机制,除了给代码带来额外的复杂性外,其在性能上完全占不到任何便宜。 ArrayBlockingQueue和LinkedBlockingQueue间还有一个明显的不同之处在于,前者在插入或删除元素时不会产生或销毁任何额外的对象实例,而后者则会生成一个额外的Node对象。这在长时间内需要高效并发地处理大批量数据的系统中,其对于GC的影响还是存在一定的区别。

    final Object[] items;
    final ReentrantLock lock;
    private final Condition notEmpty;
    private final Condition notFull;
    //队列中元素个数
    int count;//由于count是int类型,不能修改成两把锁。

构造方法
需要指定容量,是一个有界队列

    //默认是非公平锁
    public ArrayBlockingQueue(int capacity) {
        this(capacity, 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();
    }

PriorityBlockingQueue

支持优先级的无界阻塞队列。默认情况下元素采用自然顺序进行排序,也可以通过自定义类实现 compareTo() 方法来指定元素排序规则,或者初始化时通过构造器参数 Comparator 来指定排序规则。

SynchronousQueue

每个插入操作必须等待另一个线程的对应移除操作,每移除操作必须等待另一个线程的对应插入操作,队列没有任何内部容量。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值