Java阻塞队列Queue/Deque/condition,DelayQueue/SynchronousQueue/Exchanger并发(数据结构)

 1.ArrayBlockingQueue: 一个基于数组实现的有界阻塞队列,必须设置容量
 2.LinkedBlockingQueue: 基于链表实现的阻塞队列,容量可以选择进行设置,不设置的话,将是一个无边界的阻塞队列
 3.PriorityBlockingQueue: 一个无界的阻塞队列,使用的排序规则和PriorityQueue类似并提供了阻塞操作
 4.LinkedBlockingDeque: 一个基于双端链表的双端阻塞队列,容量可以选择进行设置
 5.BlockingQueue是一个继承自Queue的接口,在Queue的队列基础上增加了阻塞操作。
 6.LinkedBlockingDeque是一个基于链表的双端阻塞队列(底层数据结构是一个双端队列)。和LinkedBlockingQueue类似,区别在于该类实现了Deque接口,而LinkedBlockingQueue实现了Queue接口。

7.SynchronousQueue,

8.DelayQueue

> LinkedBlockingDeque和LinkedBlockingQueue

    LinkedBlockingQueue中维持两把锁,一把锁用于入队,一把锁用于出队,这也就意味着,同一时刻,只能有一个线程执行入队,其余执行入队的线程将会被阻塞;同时,可以有另一个线程执行出队,其余执行出队的线程将会被阻塞。换句话说,虽然入队和出队两个操作同时均只能有一个线程操作,但是可以一个入队线程和一个出队线程共同执行,也就意味着可能同时有两个线程在操作队列,那么为了维持线程安全,LinkedBlockingQueue使用一个AtomicInterger类型的变量表示当前队列中含有的元素个数,所以可以确保两个线程之间操作底层队列是线程安全的

-- LinkedBlockingDeque和LinkedBlockingQueue的异同:
LinkedBlockingDeque和LinkedBlockingQueue的相同点在于: 
1. 基于链表 
2. 容量可选,不设置的话,就是Int的最大值

LinkedBlockingDeque和LinkedBlockingQueue的不同点在于: 
1. 双端链表和单链表 
2. 不存在哨兵节点 
3. 一把锁+两个条件
>  LinkedBlockingDeque和ArrayBlockingQueue
 ArrayBlockingQueue的并发阻塞是通过ReentrantLock和Condition来实现的。ArrayBlockingQueue内部只有一把锁,意味着同一时刻只有一个线程能进行入队或者出队的操作。 
 ArrayBlockingQueue底层是使用一个数组实现队列的,并且在构造ArrayBlockingQueue时需要指定容量,也就意味着底层数组一旦创建了,容量就不能改变了,因此ArrayBlockingQueue是一个容量限制的阻塞队列。因此,在队列全满时执行入队将会阻塞,在队列为空时出队同样将会阻塞。
-- LinkedBlockingQueue与ArrayBlockingQueue的异同:
LinkedBlockingDeque和ArrayBlockingQueue的相同点在于:使用一把锁+两个条件维持队列的同步。 
LinkedBlockingQueue与ArrayBlockingQueue不同点:
  1. ArrayBlockingQueue底层基于定长的数组,所以容量限制了;LinkedBlockingQueue底层基于链表实现队列,所以容量可选,如果不设置,那么容量是int的最大值 
  2. ArrayBlockingQueue内部维持一把锁和两个条件,同一时刻只能有一个线程队列的一端操作;LinkedBlockingQueue内部维持两把锁和两个条件,同一时刻可以有两个线程在队列的两端操作,但同一时刻只能有一个线程在一端操作。 
  3. LinkedBlockingQueue的remove()类似方法时,由于需要对整个队列链表实现遍历,所以需要获取两把锁,对两端加锁。
> condition
 -- JUC提供了Lock可以方便的进行锁操作,但是有时候我们也需要对线程进行条件性的阻塞和唤醒,这时我们就需要condition条件变量,它就像是在线程上加了多个开关,可以方便的对持有锁的线程进行阻塞和唤醒。
 Condition主要是为了在J.U.C框架中提供和Java传统的监视器风格的wait,notify和notifyAll方法类似的功能。
-- AQS等待队列与Condition队列是两个相互独立的队列 
await()就是在当前线程持有锁的基础上释放锁资源,并新建Condition节点加入到Condition的队列尾部,阻塞当前线程 ;
signal()就是将Condition的头节点移动到AQS等待节点尾部,让其等待再次获取锁,
阻塞:await()方法中,在线程释放锁资源之后,如果节点不在AQS等待队列,则阻塞当前线程,如果在等待队列,则自旋等待尝试获取锁 ,
释放:signal()后,节点会从condition队列移动到AQS等待队列,则进入正常锁的获取流程;

AQS支持两种模式,一种是独占式,另一种是共享式。 
当使用独占式时,当一个线程得到了资源,那么其他尝试获取资源的线程将不会成功。 

当使用共享式时,当一个线程得到了资源,那么其他尝试获取资源的线程可能(也有可能不)会成功。

深入理解阻塞队列- http://blog.csdn.net/qq_19431333/article/details/72817403 

public static void main(String[] args) {
    final ReentrantLock reentrantLock = new ReentrantLock();
    final Condition condition = reentrantLock.newCondition();
 
    Thread thread = new Thread((Runnable) () -> {
            try {
                reentrantLock.lock();
                System.out.println("我要等一个新信号" + this);
                condition.wait();
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("拿到一个信号!!" + this);
            reentrantLock.unlock();
    }, "waitThread1");
 
    thread.start();
     
    Thread thread1 = new Thread((Runnable) () -> {
            reentrantLock.lock();
            System.out.println("我拿到锁了");
            try {
                Thread.sleep(3000);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
            condition.signalAll();
            System.out.println("我发了一个信号!!");
            reentrantLock.unlock();
    }, "signalThread");
     
    thread1.start();
}

> 阻塞队列 BlockingQueue
BlockingQueue深入分析:http://blog.csdn.net/vernonzheng/article/details/8247564
BlockingQueue的实现有一个特点,队列元素不接受null值。BlockingQueue这个接口在JDK中提供了很多具体实现,包括了数组、链表等实现.
  1.BlockingQueue定义的常用方法如下:
1)add(anObject):把anObject加到BlockingQueue里,即如果BlockingQueue可以容纳,则返回true,否则招聘异常
2)offer(anObject):表示如果可能的话,将anObject加到BlockingQueue里,即如果BlockingQueue可以容纳,则返回true,否则返回false.
3)put(anObject):把anObject加到BlockingQueue里,如果BlockQueue没有空间,则调用此方法的线程被阻断直到BlockingQueue里面有空间再继续.
4)poll(time):取走BlockingQueue里排在首位的对象,若不能立即取出,则可以等time参数规定的时间,取不到时返回null
5)take():取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到Blocking有新的对象被加入为止
其中:BlockingQueue 不接受null 元素。试图add、put 或offer 一个null 元素时,某些实现会抛出NullPointerException。null 被用作指示poll 操作失败的警戒值。
 
  2、BlockingQueue的几个注意点:
【1】BlockingQueue 可以是限定容量的。它在任意给定时间都可以有一个remainingCapacity,超出此容量,便无法无阻塞地put 附加元素。没有任何内部容量约束的BlockingQueue 总是报告Integer.MAX_VALUE 的剩余容量。
【2】BlockingQueue 实现主要用于生产者-使用者队列,但它另外还支持Collection 接口。因此,举例来说,使用remove(x) 从队列中移除任意一个元素是有可能的。然而,这种操作通常不 会有效执行,只能有计划地偶尔使用,比如在取消排队信息时。
【3】BlockingQueue 实现是线程安全的。所有排队方法都可以使用内部锁或其他形式的并发控制来自动达到它们的目的。然而,大量的 Collection 操作(addAll、containsAll、retainAll 和removeAll)没有 必要自动执行,除非在实现中特别说明。因此,举例来说,在只添加了c 中的一些元素后,addAll(c) 有可能失败(抛出一个异常)。
【4】BlockingQueue 实质上不支持使用任何一种“close”或“shutdown”操作来指示不再添加任何项。这种功能的需求和使用有依赖于实现的倾向。例如,一种常用的策略是:对于生产者,插入特殊的end-of-stream 或poison 对象,并根据使用者获取这些对象的时间来对它们进行解释。

> ArrayBlockingQueue
1. 数组实现的ArrayBlockingQueue
  看下ArrayBlockingQueue的构造方法,一共有三个:
 ArrayBlockingQueue(int capacity);
 ArrayBlockingQueue(int capacity, boolean fair);
 ArrayBlockingQueue(int capacity, boolean fair, Collection<? extends E> c);
  我们发现,构造方法中并没有无参的,这意味着队列的容量是没有默认的,在使用的时候需要给出容量值。
  后两个构造方法还有fair这个参数。这个fair可谓是似曾相识,其实它就是ReentrantLock对象初始化要用到的那个参数。我们知道ArrayBlockingQueue既然是阻塞队列,那么一定会有阻塞和唤醒,这里的实现用到的是Condition的await()和signal() / signalAll(),而用Condition的前提就是有对应的Lock对象,在ArrayBlockingQueue实现中,take()和put()用的是统一的一个单锁。在ArrayBlockingQueue的某些并发操作方法中,是需要加锁来保证线程安全的,而这就是fair参数的作用。
  另外,ArrayBlockingQueue类我们直接理解就是数组实现的阻塞队列。没错,其中的数据元素是用Object[]来保存的。对于take()和put()方法,则是分别使用了takeIndex和putIndex这两个索引值来记录存放数据的位置。
public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == 0)
            notEmpty.await();
        return extract();
    } finally {
        lock.unlock();
    }
}
  如上,是take()方法实现的源码。逻辑很简单,先加锁,然后判断是否队列已空,如条件为真,则阻塞,然后取出队列中的元素。我们看到,阻塞是通过对notEmpty这个Condition对象的await()方法调用来做到的,与此对应,extract()方法中实际上也有一个notFull.signal()的调用。

ArrayBlockingQueue创建的时候需要指定容量capacity(可以存储的最大的元素个数,因为它不会自动扩容)。其中一个构造方法为:
public ArrayBlockingQueue(int capacity, boolean fair) {  
       if (capacity <= 0)  
           throw new IllegalArgumentException();  
       this.items = (E[]) new Object[capacity];  
       lock = new ReentrantLock(fair);  
       notEmpty = lock.newCondition();  
       notFull =  lock.newCondition();  

ArrayBlockingQueue类中定义的变量有:
/** The queued items  */  
private final E[] items;  
/** items index for next take, poll or remove */  
private int takeIndex;  
/** items index for next put, offer, or add. */  
private int putIndex;  
/** Number of items in the queue */  
private int count;  
  
/* 
 * Concurrency control uses the classic two-condition algorithm 
 * found in any textbook. 
 */  
  
/** Main lock guarding all access */  
private final ReentrantLock lock;  
/** Condition for waiting takes */  
private final Condition notEmpty;  
/** Condition for waiting puts */  
private final Condition notFull;  

  使用数组items来存储元素,由于是循环队列,使用takeIndex和putIndex来标记put和take的位置。可以看到,该类中只定义了一个锁ReentrantLock,定义两个Condition对象:notEmputy和notFull,分别用来对take和put操作进行所控制。注:本文主要讲解put()和take()操作,其他方法类似。
  put(E e)方法的源码如下。进行put操作之前,必须获得锁并进行加锁操作,以保证线程安全性。加锁后,若发现队列已满,则调用notFull.await()方法,如当前线程陷入等待。直到其他线程take走某个元素后,会调用notFull.signal()方法来激活该线程。激活之后,继续下面的插入操作。
/** 
     * Inserts the specified element at the tail of this queue, waiting 
     * for space to become available if the queue is full. 
     * 
     */  
 public void put(E e) throws InterruptedException {  
        //不能存放 null  元素  
        if (e == null) throw new NullPointerException();  
        final E[] items = this.items;   //数组队列  
        final ReentrantLock lock = this.lock;  
        //加锁  
        lock.lockInterruptibly();  
        try {  
            try {  
                //当队列满时,调用notFull.await()方法,使该线程阻塞。  
                //直到take掉某个元素后,调用notFull.signal()方法激活该线程。  
                while (count == items.length)  
                    notFull.await();  
            } catch (InterruptedException ie) {  
                notFull.signal(); // propagate to non-interrupted thread  
                throw ie;  
            }  
            //把元素 e 插入到队尾  
            insert(e);  
        } finally {  
            //解锁  
            lock.unlock();  
        }  
    }  

  /** 
   * Inserts element at current put position, advances, and signals. 
   * Call only when holding lock. 
   */  
 private void insert(E x) {  
      items[putIndex] = x;    
      //下标加1或者等于0  
      putIndex = inc(putIndex);  
      ++count;  //计数加1  
      //若有take()线程陷入阻塞,则该操作激活take()线程,继续进行取元素操作。  
      //若没有take()线程陷入阻塞,则该操作无意义。  
      notEmpty.signal();  
  }  
  
  **  
   * Circularly increment i.  
   */  
  final int inc(int i) {  
      //此处可以看到使用了循环队列  
      return (++i == items.length)? 0 : i;  
  }  
  take()方法代码如下。take操作和put操作相反,故不作详细介绍。
  public E take() throws InterruptedException {  
        final ReentrantLock lock = this.lock;  
        lock.lockInterruptibly();  //加锁  
        try {  
            try {  
                //当队列空时,调用notEmpty.await()方法,使该线程阻塞。  
                //直到take掉某个元素后,调用notEmpty.signal()方法激活该线程。  
                while (count == 0)  
                    notEmpty.await();  
            } catch (InterruptedException ie) {  
                notEmpty.signal(); // propagate to non-interrupted thread  
                throw ie;  
            }  
            //取出队头元素  
            E x = extract();  
            return x;  
        } finally {  
            lock.unlock();  //解锁  
        }  
    }  

/** 
     * Extracts element at current take position, advances, and signals. 
     * Call only when holding lock. 
     */  
    private E extract() {  
        final E[] items = this.items;  
        E x = items[takeIndex];  
        items[takeIndex] = null;  
        takeIndex = inc(takeIndex);  
        --count;  
        notFull.signal();  
        return x;  
    }  
小结:进行put和take操作,共用同一个锁对象。也即是说,put和take无法并行执行!

> 单向链表队列 LinkedBlockingQueue
  LinkedBlockingQueue是JDK中BlockingQueue的有一个主要的实现。按照JavaDoc上所述,LinkedBlockingQueue是一个容量可选的阻塞队列。存在LinkedBlockingQueue()无参的默认构造方法实现,使用Integer.MAX_VALUE(128)作为默认容量。
  在LinkedBlockingQueue类的实现中,很重要的一个和ArrayBlockingQueue不同的地方,是对put()和take()分别使用了两个不同的锁,都使用了ReentrantLock实现。而针对“空”和“满”的阻塞条件,也是对这两个所对象分别构建的两个Condition对象(notEmpty和notFull),构成了双锁双条件。此外,LinkedBlockingQueue也为take和put操作分别维护了索引takeIndex和putIndex。两锁或者说队列状态的协调一致其实也是通过两个条件对象的await()和signal()来达成。
-- LinkedBlockingQueue
<<Java并发编程实践>>,有很多java.util.concurrent包下的新类。LinkedBlockingQueue就是其中之一,顾名思义这是一个阻塞的线程安全的队列,底层应该采用链表实现。
   看其API的时候发现,添加元素的方法竟然有三个:add,put,offer。
且这三个元素都是向队列尾部添加元素的意思。探究一下他们之间的差别。
1.首先看一下add方法:
  Inserts the specified element into this queue if it is possible to do so immediately without violating capacity restrictions, returning true upon success and throwing an IllegalStateException if no space is currently available. 
  This implementation returns true if offer succeeds, else throws an IllegalStateException.
   LinkedBlockingQueue构造的时候若没有指定大小,则默认大小为Integer.MAX_VALUE,当然也可以在构造函数的参数中指定大小。LinkedBlockingQueue不接受null。
   add方法在添加元素的时候,若超出了度列的长度会直接抛出异常:
public static void main(String args[]){
 try {
    LinkedBlockingQueue<String> queue=new LinkedBlockingQueue(2);
    queue.add("hello");
    queue.add("world");
    queue.add("yes");
 } catch (Exception e) {
    // TODO: handle exception
    e.printStackTrace();
 }
}
//运行结果:
java.lang.IllegalStateException: Queue full
at java.util.AbstractQueue.add(Unknown Source)
at com.wjy.test.GrandPather.main(GrandPather.java:12)
 
2.再来看一下put方法:
 Inserts the specified element at the tail of this queue, waiting if necessary for space to become available.
 对于put方法,若向队尾添加元素的时候发现队列已经满了会发生阻塞一直等待空间,以加入元素。
public static void main(String args[]){
   try {
    LinkedBlockingQueue<String> queue=new LinkedBlockingQueue(2);
    queue.put("hello");
    queue.put("world");
    queue.put("yes");
    System.out.println("yes");
   } catch (Exception e) {
    // TODO: handle exception
    e.printStackTrace();
   }
}
//运行结果:
//在queue.put("yes")处发生阻塞
//下面的“yes”无法输出
 
3.最后看一下offer方法:
     Inserts the specified element at the tail of this queue if it is possible to do so immediately without exceeding the queue's capacity, returning true upon success and false if this queue is full. When using a capacity-restricted queue, this method is generally preferable to method add, which can fail to insert an element only by throwing an exception.
   
 offer方法在添加元素时,如果发现队列已满无法添加的话,会直接返回false。
public static void main(String args[]){
  try {
    LinkedBlockingQueue<String> queue=new LinkedBlockingQueue(2);
    boolean bol1=queue.offer("hello");
    boolean bol2=queue.offer("world");
    boolean bol3=queue.offer("yes");

    System.out.println(queue.toString());
    System.out.println(bol1);
    System.out.println(bol2);
    System.out.println(bol3);
  } catch (Exception e) {
    // TODO: handle exception
    e.printStackTrace();
  }
}
//运行结果:
[hello, world]
true
true
false
 
从队列中取出并移除头元素的方法有:poll,remove,take。(队列中取元素)
poll: 若队列为空,返回null。
remove:若队列为空,抛出NoSuchElementException异常。
take:若队列为空,发生阻塞,等待有元素

/** 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();
  此外,对于队列中元素的计数,LinkedBlockingQueue也和ArrayBlockingQueue的实现略有不同,使用了AtomicInteger类对象。
  对于put()和take()以及类似的操作,双锁避免了互相影响,一定意义上看,减小了操作的锁粒度,提高了并发性。
  但对于其他操作,为了保证线程安全,都是双锁同时锁定。双锁使用要避免死锁问题,这个类实现中是统一定义了fullyLock()和fullyUnlock()的方法,先锁定的后释放,避免死锁发生的可能。
  除了用数组和队列不同数据结构对BlockingQueue接口的基本实现外,还有其他几种有特殊功能的实现。

 -- LinkedBlockingQueue 源码分析
  基于链表的阻塞队列,同ArrayListBlockingQueue类似,其内部也维持着一个数据缓冲队列(该队列由一个链表构成),当生产者往队列中放入一个数据时,队列会从生产者手中获取数据,并缓存在队列内部,而生产者立即返回;只有当队列缓冲区达到最大值缓存容量时(LinkedBlockingQueue可以通过构造函数指定该值),才会阻塞生产者队列,直到消费者从队列中消费掉一份数据,生产者线程会被唤醒,反之对于消费者这端的处理也基于同样的原理。而LinkedBlockingQueue之所以能够高效的处理并发数据,还因为其对于生产者端和消费者端分别采用了独立的锁来控制数据同步,这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据,以此来提高整个队列的并发性能。
  作为开发者,我们需要注意的是,如果构造一个LinkedBlockingQueue对象,而没有指定其容量大小,LinkedBlockingQueue会默认一个类似无限大小的容量(Integer.MAX_VALUE),这样的话,如果生产者的速度一旦大于消费者的速度,也许还没有等到队列满阻塞产生,系统内存就有可能已被消耗殆尽了。

 LinkedBlockingQueue 类中定义的变量有:
/** The capacity bound, or Integer.MAX_VALUE if none */  
private final int capacity;  
  
/** Current number of elements */  
private final AtomicInteger count = new AtomicInteger(0);  
  
/** Head of linked list */  
private transient Node<E> head;  
  
/** Tail of linked list */  
private transient Node<E> last;  
  
/** 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();  
该类中定义了两个ReentrantLock锁:putLock和takeLock,分别用于put端和take端。也就是说,生成端和消费端各自独立拥有一把锁,避免了读(take)写(put)时互相竞争锁的情况。
/** 
     * Inserts the specified element at the tail of this queue, waiting if 
     * necessary for space to become available. 
     */  
 public void put(E e) throws InterruptedException {  
        if (e == null) throw new NullPointerException();  
        // Note: convention in all put/take/etc is to preset local var  
        // holding count negative to indicate failure unless set.  
        int c = -1;  
        final ReentrantLock putLock = this.putLock;  
        final AtomicInteger count = this.count;  
        putLock.lockInterruptibly(); //加 putLock 锁  
        try {  
            /* 
             * Note that count is used in wait guard even though it is 
             * not protected by lock. This works because count can 
             * only decrease at this point (all other puts are shut 
             * out by lock), and we (or some other waiting put) are 
             * signalled if it ever changes from 
             * capacity. Similarly for all other uses of count in 
             * other wait guards. 
             */  
            //当队列满时,调用notFull.await()方法释放锁,陷入等待状态。  
            //有两种情况会激活该线程  
            //第一、 某个put线程添加元素后,发现队列有空余,就调用notFull.signal()方法激活阻塞线程  
            //第二、 take线程取元素时,发现队列已满。则其取出元素后,也会调用notFull.signal()方法激活阻塞线程  
            while (count.get() == capacity) {   
                    notFull.await();  
            }  
            // 把元素 e 添加到队列中(队尾)  
            enqueue(e);  
            c = count.getAndIncrement();  
            //发现队列未满,调用notFull.signal()激活阻塞的put线程(可能存在)  
            if (c + 1 < capacity)  
                notFull.signal();  
        } finally {  
            putLock.unlock();  
        }  
        if (c == 0)  
            //队列空,说明已经有take线程陷入阻塞,故调用signalNotEmpty激活阻塞的take线程  
            signalNotEmpty();  
    }  

/** 
 * Creates a node and links it at end of queue. 
 * @param x the item 
 */  
private void enqueue(E x) {  
    // assert putLock.isHeldByCurrentThread();  
    last = last.next = new Node<E>(x);  
}

take()方法代码如下。take操作和put操作相反,故不作详细介绍。
public E take() throws InterruptedException {  
       E x;  
       int c = -1;  
       final AtomicInteger count = this.count;  
       final ReentrantLock takeLock = this.takeLock;  
       takeLock.lockInterruptibly();  
       try {  
               while (count.get() == 0) {  
                   notEmpty.await();  
               }  
           x = dequeue();  
           c = count.getAndDecrement();  
           if (c > 1)  
               notEmpty.signal();  
       } finally {  
           takeLock.unlock();  
       }  
       if (c == capacity)  
           signalNotFull();  
       return x;  
   }

dequeue()方法如下:
/** 
 * Removes a node from head of queue. 
 * @return the node 
 */  
private E dequeue() {  
    // assert takeLock.isHeldByCurrentThread();  
    Node<E> h = head;  
    Node<E> first = h.next;  
    h.next = h; // help GC  
    head = first;  
    E x = first.item;  
    first.item = null;  
    return x;  
}  
小结:take和put操作各有一把锁,可并行读取。

> DelayQueue
DelayQueue,基本特征是容量无界,实现上单锁单条件。
  功能特点上,实际上是对优先级队列PriorityQueue类的一个封装。放入队列的元素要满足要求<E extends Delayed>。比较器是时间,因为:
  public interface Delayed extends Comparable<Delayed>
  元素需要给出getDelay()方法(实际上是Delayed接口的要求)。
  等待第一个元素的线程被设置为leader,后续线程无限期等待,直到leader通知他们。队列中的数据元素超时后,元素可被返回。

> SynchronousQueue
同步队列SynchronousQueue
  这个类在Executors中使用ThreadPoolExecutor类构造CachedThreadPool的时候被用到了。SynchronousQueue的特点是,读取操作take()和放入操作put()同时完成才会同事解开阻塞。即一个元素只有当其本身被take()的时候put()才会被唤醒。没有容量的概念。
  构造方法中可以带fair参数,分为公平和非公平实现,具体的实现分别为队列和栈,顺序不同。具体的代码实现依赖于内部类TransferQueue和TransferStack,逻辑较为复杂,这里不做细节分析。实现中的阻塞机制直接使用LockSupport的park()方法。

> Exchanger类
  这个类也是java.util.concurrent包中的,但和BlockingQueue并无直接层次结构关系。这里提到它主要是因为从用法上来看,相当于一个二项的SynchronousQueue。
具体实现上比较复杂,不做详细分析,记录下几点:
  注意到Slot和Node都是AtomicReference,其compareAndSet并不是设置node或者item,而是引用值,巧妙的利用了Node的引用值和item做数据交换
(高并发情况)实现上用了类似concurrentHashMap的segment方式,有插槽Slot的概念
阻塞机制用Locksupport.park()

> TransferQueue
  最后说下TransferQueue这个接口,这个类是java.util.concurrent包中在Java7中增加的,可以看到注释中的“@since 1.7”。和前面的不同,TransferQueue只是一个接口,不是一个实现。在JDK1.7中,有LinkedTransferQueue这样一个实现类。需要注意区分,这个TransferQueue和SynchronousQueue的内部实现类TransferQueue不是同一个类。
  这个接口/类实际上是一个比SynchronousQueue更灵活更高级的同步队列,放入新元素可以阻塞也可以非阻塞,并且也可以设定队列的元素容量。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值