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(); } }
一个基于已链接节点的、范围任意的 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();
}
}
}
}