JDK中java.util.concurrent实现了并发容器类,容器类的类图如下:
BlockingQueue为接口,ArrayBlockingQueue,DelayQueue, LinkedBlockingDeque, LinkedBlockingQueue,PriorityBlockingQueue, SynchronousQueue为实现这个接口的类。BlockingQueue提供了生成消费者和生产者模型的简单方法:
BlockingQueue 可以安全地与多个生产者和多个使用者一起使用。
使用BlockingQueue容器可以简单地操作队列,来完成生产者和消费者操作。主要的方法有take(),put(),offer()等class Producer implements Runnable { private final BlockingQueue queue; Producer(BlockingQueue q) { queue = q; } public void run() { try { while(true) { queue.put(produce()); } } catch (InterruptedException ex) { ... handle ...} } Object produce() { ... } } class Consumer implements Runnable { private final BlockingQueue queue; Consumer(BlockingQueue q) { queue = q; } public void run() { try { while(true) { consume(queue.take()); } } catch (InterruptedException ex) { ... handle ...} } void consume(Object x) { ... } } class Setup { void main() { BlockingQueue q = new SomeQueueImplementation(); Producer p = new Producer(q); Consumer c1 = new Consumer(q); Consumer c2 = new Consumer(q); new Thread(p).start(); new Thread(c1).start(); new Thread(c2).start(); } }
1.LinkedBlockingQueue
一个基于已链接节点的、范围任意的 blocking queue。此队列按 FIFO(先进先出)排序元素。队列的头部 是在队列中时间最长的元素。队列的尾部 是在队列中时间最短的元素。新元素插入到队列的尾部,并且队列获取操作会获得位于队列头部的元素。链接队列的吞吐量通常要高于基于数组的队列,但是在大多数并发应用程序中,其可预知的性能要低。
这个类的结构如下:
/** * 节点类 */ static class Node<E> { /** The item, volatile to ensure barrier separating write and read */ volatile E item; Node<E> next; Node(E x) { item = x; } } /** 容量*/ private final int capacity; /** 当前节点 为原子类型 因此此类是线程安全的 */ private final AtomicInteger count = new AtomicInteger(0); /** 头结点 */ private transient Node<E> head; /** Tail of linked list */ private transient Node<E> last; /** take操作的锁 */ private final ReentrantLock takeLock = new ReentrantLock(); /**Condition 将 Object 监视器方法(wait(),notify()等)分解成截然不同的对象,以便通过将这些对象与任意Lock实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。 这里分别是判断队列是否为空和满的Condition*/ private final Condition notEmpty = takeLock.newCondition(); /** put,操作的锁 */ private final ReentrantLock putLock = new ReentrantLock(); /** 同上 */ private final Condition notFull = putLock.newCondition();
此类主要的函数有:
offer(E)
peek()
poll()
put(E)
take()
以take()为例分析其实现:
public E take() throws InterruptedException { E x; int c = -1; final AtomicInteger count = this.count; final ReentrantLock takeLock = this.takeLock; takeLock.lockInterruptibly(); try { try { while (count.get() == 0) notEmpty.await(); } catch (InterruptedException ie) { notEmpty.signal(); // propagate to a non-interrupted thread throw ie; } x = extract(); c = count.getAndDecrement(); if (c > 1) notEmpty.signal(); } finally { takeLock.unlock(); } if (c == capacity) signalNotFull(); return x; }
首先通过takelock对访问同步,然后红色的部分就是我们平时不用并发容器时的代码,这里将其封装起来了,从而确保其并发的可靠性。要注意使用while()来判断是否要wait()。
最后附上使用并发容器的生产者-消费者模型
package mutiple_thread; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; public class Consumer_BlockQueue { public static void main(String[] args) { BlockingQueue<Integer> DataQueue = new LinkedBlockingQueue<Integer>(); Thread pt = new Thread(new Producer(DataQueue)); pt.start(); Thread ct = new Thread(new Customer(DataQueue)); ct.start(); } } class Customer implements Runnable { private BlockingQueue<Integer> DataQueue; public Customer(BlockingQueue<Integer> DataQueue) { this.DataQueue = DataQueue; } @Override public void run() { Integer i; while (!Thread.interrupted()){ try { i = DataQueue.take(); System.out.println("消费:" + i); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Producer implements Runnable { private BlockingQueue<Integer> DataQueue; private static int i = 0; public Producer(BlockingQueue<Integer> DataQueue) { this.DataQueue = DataQueue; } @Override public void run() { while (!Thread.interrupted()){ try { DataQueue.add(new Integer(++i)); System.out.println("生产:" + i); TimeUnit.MILLISECONDS.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }