并发队列

ConcurrentLinkedQueue

ConcurrentLinkedQueue并发无阻塞队列,BlockingQueue并发阻塞队列,均实现自Queue接口 

ConcurrentLinkedQueue无阻塞、无锁、高性能、无界、线程安全,性能优于BlockingQueue、不允许null值
/**
 * ConcurrentLinkedQueue
 * 无阻赛、无锁、高性能、无界队列(直至内存耗尽)、线程安全,性能优于BlockingQueue、不允许null值
 * 使用CAS算法进行入队和出队操作
 */
public class DemoThread29 {
   
   public static void main(String[] args) {

      ConcurrentLinkedQueue<Integer> queue = new ConcurrentLinkedQueue<Integer>();
      
      queue.add(1);
      queue.add(2); //add方法实际调用了offer方法

      //offer方法与add没有区别
      queue.offer(3);
      queue.offer(4);
      
      //queue.add(null); //不允许添加null元素
      System.out.println(queue);

      System.out.println("[1]peek="+queue.peek()); //读取头元素,但是不移除
      System.out.println("[2]size="+queue.size()); //peek方法不会导致size改变

      System.out.println("[3]poll="+queue.poll()); //读取头元素,并且移除
      System.out.println("[4]size="+queue.size()); //poll方法导致size改变
      
      System.out.println("[5]poll="+queue.poll()); 
      System.out.println("[6]poll="+queue.poll()); 
      System.out.println("[7]poll="+queue.poll()); 
      System.out.println("[8]size="+queue.size()); 
      
      System.out.println("peek="+queue.peek()); //队列为空, 读取头元素,返回null
      System.out.println("pool="+queue.poll()); //队列为空, 读取头元素并移除, 返回null
   }
}

运行结果

[1, 2, 3, 4]
[1]peek=1
[2]size=4
[3]poll=1
[4]size=3
[5]poll=2
[6]poll=3
[7]poll=4
[8]size=0
peek=null
pool=null

ArrayBlockingQueue

ArrayBlockingQueue:基于数组实现的阻塞有界队列、创建时可指定长度,内部实现维护了一个定
长数组用于缓存数据,内部没有采用读写分离,写入和读取数据不能同时进行(COW容器读写可同时进行),不允许null值

采用数组存储

/** The queued items */
final Object[] items;

查看add方法

/**
 * 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 {@code true} upon success and throwing an
 * {@code IllegalStateException} if this queue is full.
 *
 * @param e the element to add
 * @return {@code true} (as specified by {@link Collection#add})
 * @throws IllegalStateException if this queue is full
 * @throws NullPointerException if the specified element is null
 */
public boolean add(E e) {
    return super.add(e);
}

调用了父类的add方法

public boolean add(E e) {
    if (offer(e))
        return true;
    else
        throw new IllegalStateException("Queue full");
}

调用了offer方法

/**
 * Inserts the specified element into this queue if it is possible to do
 * so immediately without violating capacity restrictions.
 * When using a capacity-restricted queue, this method is generally
 * preferable to {@link #add}, which can fail to insert an element only
 * by throwing an exception.
 *
 * @param e the element to add
 * @return {@code true} if the element was added to this queue, else
 *         {@code false}
 * @throws ClassCastException if the class of the specified element
 *         prevents it from being added to this queue
 * @throws NullPointerException if the specified element is null and
 *         this queue does not permit null elements
 * @throws IllegalArgumentException if some property of this element
 *         prevents it from being added to this queue
 */
boolean offer(E e);

查看ArrayBlockingQueue实现的方法,使用ReentrantLock(重入锁)控制线程安全

/**
 * 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 {@code true} upon success and {@code false} if this queue
 * is full.  This method is generally preferable to method {@link #add},
 * which can fail to insert an element only by throwing an exception.
 *
 * @throws NullPointerException if the specified element is null
 */
public boolean offer(E e) {
    checkNotNull(e);
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        if (count == items.length)
            return false;
        else {
            enqueue(e);
            return true;
        }
    } finally {
        lock.unlock();
    }
}

使用

/**
 * BlockingQueue的实现类之ArrayBlockingQueue
 * 基于数组实现的阻塞有界队列、创建时可指定长度,内部实现维护了一个定长数组用于缓存数据,
 * 内部没有采用读写分离,add和poll数据不能同时进行,可以指定先进先出或后进先出。 使用ReentrantLock(重入锁)控制线程安全
 * --------------------------------------------------- 
 *  offer 如果队列已经满了,则不阻塞,不抛出异常
 *  offer 可设置最大阻塞时间,2秒,如果队列还是满的,则不阻塞,不抛出异常
 *  add 如果队列满了,则不阻塞,直接抛出异常 
 *  put 如果队列满了,则永远阻塞, 不抛出异常 
 * --------------------------------------------------- 
 * peek 读取头元素不移除,队列为空,返回null,不阻塞, 不抛异常 
 * poll 读取头元素并移除,队列为空,返回null,不阻塞, 不抛异常 
 * poll 可指定阻塞时间,2秒,如果队列依然为空,则返回null,不抛异常 
 * take 读取头元素并移除,如果队列为空,则永远阻塞,不抛出异常 
 * drainTo 取出queue中指定个数(或全部)的元素放入list中,并移除,当队列为空时不抛出异常
 */
public class DemoThread30 {

   // 测试各种添加元素的方法
   public static void testAdd() {
      ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(3);
      queue.add(1);
      queue.offer(2);
//    try {
//       queue.add(null); //不允许添加null元素
//    } catch (Exception e1) {
//       System.out.println("queue.add(null)异常"+e1.getMessage());
//    }
      try {
         queue.put(3);
      } catch (InterruptedException e2) {
         e2.printStackTrace();
      }
      try {
         queue.add(4); // 如果队列满了,则抛出异常
      } catch (Exception e1) {
         System.out.println("queue.add(4)异常"+e1.getMessage());
      }
      System.out.println("1>>" + queue);
      queue.offer(4); // 如果队列已经满了,则不阻塞,不抛出异常
      System.out.println("queue.offer(4)>>" + queue);
      try {
         // 可设置最大阻塞时间,5秒,如果队列还是满的,则不阻塞,不抛出异常
         queue.offer(6, 2, TimeUnit.SECONDS);
         System.out.println("queue.offer(6, 2, TimeUnit.SECONDS)>>" + queue);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }

      try {
         queue.put(7); // 如果队列满了,则永远阻塞, 不抛出异常
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
      System.out.println(">>" + queue);

   }

   // 测试获取数据
   public static void testTake1() {
      ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(2);
      queue.add(1);
      queue.add(2);

      System.out.println("1>>" + queue.peek()); // 读取头元素不移除
      System.out.println(queue);

      System.out.println("2>>" + queue.poll()); // 读取头元素,并移除
      System.out.println("3>>" + queue);

      try {
         // 获取头元素,并移除数据
         System.out.println("4>>" + queue.take());
         System.out.println("5>>" + queue);
      } catch (InterruptedException e1) {
         e1.printStackTrace();
      }

      System.out.println("6>>" + queue.peek()); // 队列为空,返回null,不阻塞, 不抛异常
      System.out.println("7>>" + queue.poll()); // 队列为空,返回null,不阻塞, 不抛异常

      try {
         // 可指定阻塞时间,2秒,如果队列依然为空,则返回null,不抛异常
         System.out.println("8>>" + queue.poll(2, TimeUnit.SECONDS));
      } catch (InterruptedException e) {
         e.printStackTrace();
      }

      try {
         queue.take(); // 如果队列为空,则永远阻塞,不抛出异常
      } catch (InterruptedException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }
      System.out.println("9>>over");
   }

   // 测试获取数据2
   public static void testTake2() {
      ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(3);
      queue.add(1);
      queue.add(2);
      queue.add(3);

      ArrayList<Integer> list = new ArrayList<Integer>();
      // 英文 drain 喝光,喝干; 使(精力、金钱等)耗尽; 使流出; 排掉水;
      queue.drainTo(list, 2); // 取出queue中指定个数的元素放入list中,并移除
      System.out.println(list);
      System.out.println(queue);
   }

   // 测试获取数据3
   public static void testTake3() {
      ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(3);
      queue.add(1);
      queue.add(2);
      queue.add(3);

      ArrayList<Integer> list = new ArrayList<Integer>();
      queue.drainTo(list); // 取出queue中的全部元素放入list中,并移除
      System.out.println("1>>" + list);
      System.out.println("2>>" + queue);

      ArrayList<Integer> list1 = new ArrayList<Integer>();
      queue.drainTo(list1); // 当队列为空时不抛出异常
      System.out.println("3>>" + list1);
      System.out.println("4>>" + queue);
   }

   public static void main(String[] args) {
      testAdd();
//    testTake1();
//    testTake2();
//    testTake3();
   }
}

testAdd测试结果,测试队列满了之后使用各个函数添加数据

queue.add(4)异常Queue full
1>>[1, 2, 3]
queue.offer(4)>>[1, 2, 3]
queue.offer(6, 2, TimeUnit.SECONDS)>>[1, 2, 3]

testTake1测试结果

1>>1
[1, 2]
2>>1
3>>[2]
4>>2
5>>[]
6>>null
7>>null
8>>null

testTake2测试结果,将数据转移到另一个容器

[1, 2]
[3]

drainTo的时候队列为空,不会阻塞,也不会抛出异常

1>>[1, 2, 3]
2>>[]
3>>[]
4>>[]

LinkedBlockingQueue

LinkedBlockingQueue :基于链表的阻塞队列,内部维护一个链表存储缓存数据, 支持写入和读取
的并发操作, 创建时可指定长度也可以不指定,不指定时代表无界队列, 不允许null值
使用ReentrantLock(重入锁)控制线程安全
不允许插入null
/**
 * BlockingQueue的实现类之LinkedBlockingDeque 
 * 基于链表的阻塞队列,内部维护一个链表存储缓存数据
 * 内部采用读写分离的锁机制,所以支持写入和读取的并发操作
 *  创建时可指定长度也可以不指定,不指定时代表无界队列 
 *  不允许null值
 * --------------------------------------------------- 
 * offer 如果队列已经满了,则不阻塞,不抛出异常
 * offer 可设置最大阻塞时间,2秒,如果队列还是满的,则不阻塞,不抛出异常 
 * add 如果队列满了,则不阻塞,直接抛出异常 
 * put 如果队列满了,则永远阻塞, 不抛出异常 
 * --------------------------------------------------- 
 * peek 读取头元素不移除,队列为空,返回null,不阻塞, 不抛异常
 * poll 读取头元素并移除,队列为空,返回null,不阻塞, 不抛异常 
 * poll 可指定阻塞时间,2秒,如果队列依然为空,则返回null,不抛异常 
 * take 读取头元素并移除,如果队列为空,则永远阻塞,不抛出异常
 * drainTo 取出queue中指定个数(或全部)的元素放入list中,并移除,当队列为空时不抛出异常
 */
public class DemoThread31 {

   // 测试各种添加元素的方法
   public static void testAdd() {
      LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>(3);
      queue.add(1);
      queue.offer(2);
      //queue.add(null); //不允许添加null元素
      try {
         queue.put(3);
      } catch (InterruptedException e1) {
         e1.printStackTrace();
      }

      System.out.println("1>>"+queue);
      queue.offer(4); // 如果队列已经满了,则不阻塞,不抛出异常
      System.out.println("2>>"+queue);
      try {
         // 可设置最大阻塞时间,2秒,如果队列还是满的,则不阻塞,不抛出异常
         queue.offer(6, 2, TimeUnit.SECONDS);
         System.out.println("3>>"+queue);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }

      queue.add(7); //如果队列满了,则不阻塞,直接抛出异常
      // System.out.println(queue);

      try {
         queue.put(7); // 如果队列满了,则永远阻塞, 不抛出异常
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
      System.out.println("4>>"+queue);

   }

   // 测试获取数据1
   public static void testTake1() {
      LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>(2);
      queue.add(1);
      queue.add(2);

      System.out.println("1>>"+queue.peek()); // 读取头元素不移除
      System.out.println("2>>"+queue);

      System.out.println("3>>"+queue.poll()); // 读取头元素,并移除
      System.out.println("4>>"+queue);

      try {
         // 获取头元素,并移除数据
         System.out.println("5>>"+queue.take());
         System.out.println("6>>"+queue);
      } catch (InterruptedException e1) {
         e1.printStackTrace();
      }

      System.out.println("7>>"+queue.peek()); // 队列为空,返回null,不阻塞, 不抛异常
      System.out.println("8>>"+queue.poll()); // 队列为空,返回null,不阻塞, 不抛异常

      try {
         // 可指定阻塞时间,2秒,如果队列依然为空,则返回null,不抛异常
         System.out.println("9>>"+queue.poll(2, TimeUnit.SECONDS));
      } catch (InterruptedException e) {
         e.printStackTrace();
      }

      try {
         queue.take(); // 如果队列为空,则永远阻塞,不抛出异常
      } catch (InterruptedException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }
   }

   // 测试获取数据2
   public static void testTake2() {
      LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>();
      queue.add(1);
      queue.add(2);
      queue.add(3);

      ArrayList<Integer> list = new ArrayList<Integer>();
      queue.drainTo(list, 2); //取出queue中指定个数的元素放入list中,并移除
      System.out.println(list);
      System.out.println(queue);
   }

   // 测试获取数据3
   public static void testTake3() {
      LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>(3);
      queue.add(1);
      queue.add(2);
      queue.add(3);

      ArrayList<Integer> list = new ArrayList<Integer>();
      queue.drainTo(list); //取出queue中的全部元素放入list中,并移除
      System.out.println(list);
      System.out.println(queue);
      
      ArrayList<Integer> list1 = new ArrayList<Integer>();
      queue.drainTo(list1); //当队列为空时不抛出异常
      System.out.println(list1);
      System.out.println(queue);
   }

   public static void main(String[] args) {
       testAdd();
//     testTake1();
//     testTake2();
//     testTake3();
   }
}

执行testAdd方法,LinkedBlockingQueue满了执行add不阻塞,直接抛出异常

1>>[1, 2, 3]
2>>[1, 2, 3]
3>>[1, 2, 3]
Exception in thread "main" java.lang.IllegalStateException: Queue full
	at java.util.AbstractQueue.add(AbstractQueue.java:98)
	at com.mimaxueyuan.demo.middle.DemoThread31.testAdd(DemoThread31.java:66)
	at com.mimaxueyuan.demo.middle.DemoThread31.main(DemoThread31.java:148)

不指定初始容量,就是无界的,执行结果如下

1>>[1, 2, 3]
2>>[1, 2, 3, 4]
3>>[1, 2, 3, 4, 6]
4>>[1, 2, 3, 4, 6, 7, 7]

执行testTake1结果如下

1>>1
2>>[1, 2]
3>>1
4>>[2]
5>>2
6>>[]
7>>null
8>>null
9>>null

执行testTake2,测试引流

[1, 2]
[3]

执行testTake3,测试引流队列为空的情况,不抛出异常

[1, 2, 3]
[]
[]
[]

SynchronousQueue

SynchronousQueue :没有任何容量,必须现有线程先从队列中take,才能向queue中add数据,否
则会抛出队列已满的异常。不能使用peek方法取数据,此方法底层没有实现,会直接返回null
不允许null
优点:准确高效地在线程之间传递数据,由于是没有容量的,像一个标记或者传送门
举例:t2线程向SynchronousQueue take数据,队列为空就被阻塞了,t1线程向SynchronousQueue加入数据的时候没有经过存储,直接被t2线程取走了
/**
 * BlockingQueue的实现类之SynchronousQueue
 * 没有任何容量,必须现有线程先从队列中take,才能向queue中add数据,否则会抛出队列已满的异常。
 * 不能使用peek方法取数据,此方法底层没有实现,会直接返回null
 * ---------------------------------------
 * 如果没有读取线程,则add方法会排除Queue Full异常,所以建议使用put方法,进行阻塞。
 * 如果没有写入线程,则poll方法会无法取到数据,所以建议设置poll方法的阻塞时长,或者使用take方法进行永久阻塞
 */
public class DemoThread32 {

   /**
    *  测试-SynchronousQueue没有容量,第一次add就会抛出异常
    *
    * @author yin.hl
    * @Title: test1 
    * @return: void
    */
   public static void test1() {
      SynchronousQueue<Integer> queue = new SynchronousQueue<Integer>();
      queue.add(1);
   }

   /**
    *  测试-SynchronousQueue没有容量,使用put方法阻塞,避免抛出异常
    *
    * @author yin.hl
    * @Title: test2 
    * @return: void
    */
   public static void test2() {
      SynchronousQueue<Integer> queue = new SynchronousQueue<Integer>();
      try {
         queue.put(1);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }

   /**
    * 测试:一个线程put一个线程take,如果没有数据则take线程永远阻塞
    *
    * @author yin.hl
    * @Title: test3 
    * @return: void
    */
   public static void test3() {
      final SynchronousQueue<Integer> queue = new SynchronousQueue<Integer>();
      new Thread(new Runnable() {
         @Override
         public void run() {
            while (true) {
               try {
                  // 使用get方法当队列为空时阻塞
                  System.out.println("take->"+queue.take());
               } catch (InterruptedException e) {
                  e.printStackTrace();
               }
            }
         }
      }).start();

      new Thread(new Runnable() {
         @Override
         public void run() {
            try {
               // 使用put方法,当队列满的时候阻塞
               queue.put(1);
               queue.put(2);
               queue.put(3);
               queue.put(4);
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
         }
      }).start();
   }
   
   /**
    * 测试:一个线程put一个线程take, take线程最多阻塞5秒,如果还没有取到数据,则结束线程
    *
    * @author yin.hl
    * @Title: test4 
    * @return: void
    */
   public static void test4() {
      final SynchronousQueue<Integer> queue = new SynchronousQueue<Integer>();
      new Thread(new Runnable() {
         @Override
         public void run() {
            while (true) {
               try {
                  //最多阻塞5秒,如果还没有取到数据,则结束线程
                  Integer result = queue.poll(5, TimeUnit.SECONDS);
                  System.out.println("poll->"+result);
                  if(result==null){
                     System.out.println("poll->5s没有数据,线程stop");
                     break;
                  }
               } catch (InterruptedException e) {
                  e.printStackTrace();
               }
            }
         }
      }).start();

      new Thread(new Runnable() {
         @Override
         public void run() {
            try {
               // 使用put方法,当队列满的时候阻塞
               queue.put(1);
               queue.put(2);
               queue.put(3);
               queue.put(4);
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
         }
      }).start();
   }

   public static void main(String[] args) {
//    test1();
//    test2();
//    test3();
//    test4();
   }
}

执行test1,结果如下

Exception in thread "main" java.lang.IllegalStateException: Queue full
	at java.util.AbstractQueue.add(AbstractQueue.java:98)
	at com.mimaxueyuan.demo.middle.DemoThread32.test1(DemoThread32.java:47)
	at com.mimaxueyuan.demo.middle.DemoThread32.main(DemoThread32.java:150)

执行test2,没有进程取数据,进程进入阻塞状态

执行test3,数据取完后进入阻塞状态,执行结果如下

take->1
take->2
take->3
take->4

执行test4,结果如下,一个线程put一个线程take, take线程最多阻塞5秒,如果还没有取到数据,则结束线程

poll->1
poll->2
poll->3
poll->4
poll->null
poll->5s没有数据,线程stop

PriorityBlockingQueue

PriorityBlockingQueue:一个无界阻塞队列,默认初始化长度11,也可以手动指定,但是队列会自
动扩容。资源被耗尽时导致 OutOfMemoryError。不允许使用 null元素。不允许插入不可比较的对
象(导致抛出 ClassCastException), 加入的对象实现Comparable接口
不允许添加null元素

查看源码

基于数组实现

/**
 * Priority queue represented as a balanced binary heap: the two
 * children of queue[n] are queue[2*n+1] and queue[2*(n+1)].  The
 * priority queue is ordered by comparator, or by the elements'
 * natural ordering, if comparator is null: For each node n in the
 * heap and each descendant d of n, n <= d.  The element with the
 * lowest value is in queue[0], assuming the queue is nonempty.
 */
private transient Object[] queue;

初始容量

/**
 * Default array capacity.
 */
private static final int DEFAULT_INITIAL_CAPACITY = 11;

构造函数

/**
 * Creates a {@code PriorityBlockingQueue} with the default
 * initial capacity (11) that orders its elements according to
 * their {@linkplain Comparable natural ordering}.
 */
public PriorityBlockingQueue() {
    this(DEFAULT_INITIAL_CAPACITY, null);
}

/**
 * Creates a {@code PriorityBlockingQueue} with the specified
 * initial capacity that orders its elements according to their
 * {@linkplain Comparable natural ordering}.
 *
 * @param initialCapacity the initial capacity for this priority queue
 * @throws IllegalArgumentException if {@code initialCapacity} is less
 *         than 1
 */
public PriorityBlockingQueue(int initialCapacity) {
    this(initialCapacity, null);
}

/**
 * Creates a {@code PriorityBlockingQueue} with the specified initial
 * capacity that orders its elements according to the specified
 * comparator.
 *
 * @param initialCapacity the initial capacity for this priority queue
 * @param  comparator the comparator that will be used to order this
 *         priority queue.  If {@code null}, the {@linkplain Comparable
 *         natural ordering} of the elements will be used.
 * @throws IllegalArgumentException if {@code initialCapacity} is less
 *         than 1
 */
public PriorityBlockingQueue(int initialCapacity,
                             Comparator<? super E> comparator) {
    if (initialCapacity < 1)
        throw new IllegalArgumentException();
    this.lock = new ReentrantLock();
    this.notEmpty = lock.newCondition();
    this.comparator = comparator;
    this.queue = new Object[initialCapacity];
}
/**
 * 阻塞队列之PriorityBlockingQueue(带有优先级的阻塞队列)
 * 
 * 一个无界阻塞队列,它使用与类 PriorityQueue 相同的顺序规则,并且提供了阻塞获取操作。虽然此队列逻辑上是无界的,但是资源被耗尽时试图执行 add 操作也将失败(导致 OutOfMemoryError)。
 * 此类不允许使用 null 元素。依赖自然顺序的优先级队列也不允许插入不可比较的对象(这样做会导致抛出 ClassCastException)。
 * 此类及其迭代器可以实现 Collection 和 Iterator 接口的所有可选 方法。
 * iterator() 方法中提供的迭代器并不 保证以特定的顺序遍历 PriorityBlockingQueue 的元素。
 * 如果需要有序地进行遍历,则应考虑使用 Arrays.sort(pq.toArray())。
 * 此外,可以使用方法 drainTo 按优先级顺序移除 全部或部分元素,并将它们放在另一个 collection 中。
 * 在此类上进行的操作不保证具有同等优先级的元素的顺序。如果需要实施某一排序,那么可以定义自定义类或者比较器,比较器可使用修改键断开主优先级值之间的联系。
 * 例如,以下是应用先进先出 (first-in-first-out) 规则断开可比较元素之间联系的一个类。
 * 要使用该类,则需要插入一个新的 FIFOEntry(anEntry) 来替换普通的条目对象。
 * 
 * 注意:加入到PriorityBlockingQueue中的元素不是立即排序的,是在调用take等读取方法之后
 * ----------------------------------------------
 * put、add方法实际调用了offer方法
 * ----------------------------------------------
 * peek 读取头元素不移除,队列为空,返回null,不阻塞, 不抛异常
 * poll 读取头元素并移除,队列为空,返回null,不阻塞, 不抛异常 
 * poll 可指定阻塞时间,2秒,如果队列依然为空,则返回null,不抛异常 
 * take 读取头元素并移除,如果队列为空,则永远阻塞,不抛出异常
 * drainTo 取出queue中指定个数(或全部)的元素放入list中,并移除,当队列为空时不抛出异常
 */
class Demo implements Comparable<Demo>{

   private Integer id;
   private String name;
   
   public Demo(Integer id, String name) {
      super();
      this.id = id;
      this.name = name;
   }

   @Override
   public String toString() {
      return "Demo [id=" + id + ", name=" + name + "]";
   }

   @Override
   public int compareTo(Demo o) {
      if(this.id<o.id){
         return -1;
      }else if(this.id>o.id){
         return 1;
      }else{
         return 0;
      }
   }
}

public class DemoThread33 {
   
   /**
    * 测试添加方法
    * @author yin.hl
    * @Title: testAdd 
    * @return: void
    */
   public static void testAdd(){
      PriorityBlockingQueue<Demo> queue = new PriorityBlockingQueue<Demo>(12);
      queue.add(new Demo(3,"a")); //实际调用了offer方法
      queue.offer(new Demo(1,"b"));
      //queue.add(null); //不允许添加null元素
      queue.put(new Demo(2,"c")); //实际调用了offer方法
      queue.offer(new Demo(4,"d"), 1, TimeUnit.SECONDS); //设定阻塞时间
      System.out.println("队列中的数据不是按照顺序排列的:"+queue);
   }
   
   public static void testTake1(){
      PriorityBlockingQueue<Demo> queue = new PriorityBlockingQueue<Demo>(12);
      queue.add(new Demo(3,"a")); //实际调用了offer方法
      queue.offer(new Demo(1,"b"));
      //queue.add(null); //不允许添加null元素
      queue.put(new Demo(2,"c")); //实际调用了offer方法
      queue.offer(new Demo(4,"d"), 1, TimeUnit.SECONDS); //设定阻塞时间
      System.out.println(queue);
      
      try {
         System.out.println("take1>>"+queue.take());
         System.out.println(queue); //take之后才进行排序
         System.out.println("poll>>"+queue.poll());
         System.out.println("take2>>"+queue.take());
         System.out.println("take3>>"+queue.take());
         
      } catch (InterruptedException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }
   }
   
   public static void testTake2(){
      PriorityBlockingQueue<Demo> queue = new PriorityBlockingQueue<Demo>(12);
      queue.add(new Demo(3,"a")); //实际调用了offer方法
      queue.offer(new Demo(1,"b"));
      System.out.println("a>>"+queue);
      
      System.out.println("b>>"+queue.peek()); // 读取头元素不移除
      System.out.println("c>>"+queue);

      System.out.println("d>>"+queue.poll()); // 读取头元素,并移除
      System.out.println("e>>"+queue);

      try {
         // 获取头元素,并移除数据
         System.out.println("f>>"+queue.take());
         System.out.println("g>>"+queue);
      } catch (InterruptedException e1) {
         e1.printStackTrace();
      }

      System.out.println("h>>"+queue.peek()); // 队列为空,返回null,不阻塞, 不抛异常
      System.out.println("i>>"+queue.poll()); // 队列为空,返回null,不阻塞, 不抛异常

      try {
         // 可指定阻塞时间,2秒,如果队列依然为空,则返回null,不抛异常
         System.out.println("j>>"+queue.poll(2, TimeUnit.SECONDS));
      } catch (InterruptedException e) {
         e.printStackTrace();
      }

      try {
         queue.take(); // 如果队列为空,则永远阻塞,不抛出异常
      } catch (InterruptedException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }
      
      System.out.println("over");
   }
   
   // 测试获取数据2
   public static void testTake3() {
      LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>(5);
      queue.add(3);
      queue.add(2);
      queue.add(3);
      queue.add(1);
      queue.add(5);

      ArrayList<Integer> list = new ArrayList<Integer>();
      queue.drainTo(list, 4); // 取出queue中指定个数的元素放入list中,并移除
      System.out.println("a>>"+list);
      System.out.println("b>>"+queue);
      Object[] array = list.toArray();
      Arrays.sort(array);
      System.out.println("sort>>"+Arrays.toString(array));
   }

   // 测试获取数据3
   public static void testTake4() {
      PriorityBlockingQueue<Integer> queue = new PriorityBlockingQueue<Integer>(3);
      queue.add(3);
      queue.add(2);
      queue.add(1);

      ArrayList<Integer> list = new ArrayList<Integer>();
      queue.drainTo(list); // 取出queue中的全部元素放入list中,并移除
      System.out.println("a>>"+list);
      System.out.println("b>>"+queue);
      Object[] array = list.toArray();
      Arrays.sort(array);
      System.out.println("sort>>"+Arrays.toString(array));

      ArrayList<Integer> list1 = new ArrayList<Integer>();
      queue.drainTo(list1); // 当队列为空时不抛出异常
      System.out.println("c>>"+list1);
      System.out.println("d>>"+queue);
   }
      
   public static void main(String[] args) {
//    testAdd();
//    testTake1();
//    testTake2();
//    testTake3();
      testTake4();
   }
}

执行testAdd,队列中的数据不是按照顺序排列的

队列中的数据不是按照顺序排列的:[Demo [id=1, name=b], Demo [id=3, name=a], Demo [id=2, name=c], Demo [id=4, name=d]]

执行testTake1,优先级队列并不是在add时候计算优先级的,在取数据的时候才会计算优先级

[Demo [id=1, name=b], Demo [id=3, name=a], Demo [id=2, name=c], Demo [id=4, name=d]]
take1>>Demo [id=1, name=b]
[Demo [id=2, name=c], Demo [id=3, name=a], Demo [id=4, name=d]]
poll>>Demo [id=2, name=c]
take2>>Demo [id=3, name=a]
take3>>Demo [id=4, name=d]

执行testTake2,take取不到数据,阻塞了

a>>[Demo [id=1, name=b], Demo [id=3, name=a]]
b>>Demo [id=1, name=b]
c>>[Demo [id=1, name=b], Demo [id=3, name=a]]
d>>Demo [id=1, name=b]
e>>[Demo [id=3, name=a]]
f>>Demo [id=3, name=a]
g>>[]
h>>null
i>>null
j>>null

执行testTake3,如何对PriorityBlockingQueue排序?先取出再Arrays.toString

a>>[3, 2, 3, 1]
b>>[5]
sort>>[1, 2, 3, 3]

执行testTake4,drainTo在队列为空时不报异常

a>>[1, 2, 3]
b>>[]
sort>>[1, 2, 3]
c>>[]
d>>[]

DelayQueue

DelayQueue:Delayed 元素的一个无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列
的头部 是延迟期满后保存时间最长的 Delayed 元素。如果延迟都还没有期满,则队列没有头部,
并且 poll 将返回 null。当一个元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一个小于等于
0 的值时,将发生到期。即使无法使用 take 或 poll 移除未到期的元素,也不会将这些元素作为正
常元素对待。例如,size 方法同时返回到期和未到期元素的计数。此队列不允许使用 null 元素。内
部元素需实现Delayed接口
场景:缓存到期删除、任务超时处理、空闲链接关闭等
基于优先级阻塞队列实现的

底层使用了PriorityQueue

public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
    implements BlockingQueue<E> {

    private final transient ReentrantLock lock = new ReentrantLock();
    private final PriorityQueue<E> q = new PriorityQueue<E>();
    
    //省略...
}

Delayed接口,只有几个计算元素是否过期的方法

/**
 * A mix-in style interface for marking objects that should be
 * acted upon after a given delay.
 *
 * <p>An implementation of this interface must define a
 * {@code compareTo} method that provides an ordering consistent with
 * its {@code getDelay} method.
 *
 * @since 1.5
 * @author Doug Lea
 */
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);
}

例子

/**
 * 阻塞队列之DelayQueue(带有延迟功能的阻塞队列)
 * 
 * Delayed 元素的一个无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部 是延迟期满后保存时间最长的 Delayed 元素。
 * 如果延迟都还没有期满,则队列没有头部,并且 poll 将返回 null。
 * 当一个元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一个小于等于 0 的值时,将发生到期。
 * 即使无法使用 take 或 poll 移除未到期的元素,也不会将这些元素作为正常元素对待。
 * 例如,size 方法同时返回到期和未到期元素的计数。此队列不允许使用 null 元素。
 * 内部元素需实现Delayed接口
 * ----------------------------------------------
 * Demo说明:模拟一种游戏的自动退出机制,例如甲乙丙三个人分别可以免费玩游戏3、2、1秒,到时自动踢出系统
 * ----------------------------------------------
 * ----------------------------------------------
 */
class User implements Delayed{

   private int id; 
   private String name;
   private long endTime; //退出时间
   
   public User(int id, String name, long endTime) {
      this.id = id;
      this.name = name;
      this.endTime = endTime;
   }
   public int getId() {
      return id;
   }
   public void setId(int id) {
      this.id = id;
   }
   public String getName() {
      return name;
   }
   public void setName(String name) {
      this.name = name;
   }
   public long getEndTime() {
      return endTime;
   }
   public void setEndTime(long endTime) {
      this.endTime = endTime;
   }
   @Override
   public String toString() {
      return this.name;
   }
   
   //由于需要根据延时时间的长短,计算从数据中移除的顺序,所以需要实现compareTo方法计算优先级,类似优先级队列
   @Override
   public int compareTo(Delayed o) {
      User user = (User)o;
      if(this.endTime>user.getEndTime()){
         return 1;
      }else if(this.endTime<user.getEndTime()){
         return -1;
      }else{
         return 0;
      }
   }

   //计算剩余延迟时间;零或负值指示延迟时间已经用尽
   @Override
   public long getDelay(TimeUnit unit) {
      return this.endTime-System.currentTimeMillis();
   }
   
}

public class DemoThread34 {
   
   DelayQueue<User> delayQueue = new DelayQueue<User>();
   
   //登录游戏,加入队列
   public void login(User user){
      delayQueue.add(user);
      System.out.println("用户("+user.getId()+")"+user.getName()+"已登录,预计下机时间为"+user.getEndTime());
   }
   
   //退出游戏,移除队列
   public void logout(){
      try {
         System.out.println(delayQueue);
         User user = delayQueue.take();
         //User user = delayQueue.poll(); //不能使用poll方法,因为没有阻塞功能
         System.out.println("用户("+user.getId()+")"+user.getName()+"到时自动退出,时间为"+System.currentTimeMillis()+"...");
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }
   
   //获取当前在线人数
   public int onlineSize(){
      return delayQueue.size();
   }
   
   public static void main(String[] args) {
      DemoThread34 demo = new DemoThread34();
      
      //用户登录,并设置退出时间
      demo.login(new User(1,"甲",30000+System.currentTimeMillis()));
      demo.login(new User(2,"乙",20000+System.currentTimeMillis()));
      demo.login(new User(3,"丙",10000+System.currentTimeMillis()));
      
      while(true){
         //监控到时用户
         demo.logout();
         //如果在线用户则退出
         if(demo.onlineSize()==0){
            break;
         }
      }
      
   }
}

执行结果

用户(1)甲已登录,预计下机时间为1614327308932
用户(2)乙已登录,预计下机时间为1614327298932
用户(3)丙已登录,预计下机时间为1614327288932
[丙, 甲, 乙]
用户(3)丙到时自动退出,时间为1614327288932...
[乙, 甲]
用户(2)乙到时自动退出,时间为1614327298932...
[甲]
用户(1)甲到时自动退出,时间为1614327308932...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值