JDK的Concurrent包中的BlockingQueue接口,增加了可阻塞的插入和获取等操作。当队列为空时,获取操作被阻塞;当队列满时,插入操作被阻塞。通过BlockingQueue可以和方便的实现生产者与消费者模式。
LinkedBlockingQueue(一个BlockingQueue的实现类)通过分别在插入和获取操作上加不同的Reentrant锁,来实现插入操作的同步和获取操作的同步,即插入操作和获取操作是可以同时进行的。如下是根据LinkedBlockingQueue源码自己实现的一个简单的阻塞队列:
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class MyBlockQueue {
private LinkedList queue = new LinkedList();
private final ReentrantLock putLock = new ReentrantLock();
private final ReentrantLock takeLock = new ReentrantLock();
private final Condition empty = takeLock.newCondition();
private final Condition full = putLock.newCondition();
private final int capacity = 5;
private AtomicInteger count = new AtomicInteger();
/*释放因列表满而等待的线程*/
protected void signalFull() {
final ReentrantLock putLock = this.putLock;
try {
putLock.lockInterruptibly();
full.signal();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally {
putLock.unlock();
}
}
/*释放因列表空而阻塞的读线程*/
protected void signalEmpty() {
final ReentrantLock takeLock = this.takeLock;
try {
takeLock.lockInterruptibly();
empty.signal();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally {
takeLock.unlock();
}
}
public void put(E e) {
if(e == null) return;
final ReentrantLock putLock = this.putLock;
int c = -1;
try {
putLock.lockInterruptibly();
final Condition full = this.full;
final AtomicInteger count = this.count;
while(count.get() == capacity) {
full.await();
}
queue.add(e);
c = count.getAndIncrement();//获取更新之前的值
if(c+1 < capacity) {//进行put操作后队列未满,释放full
full.signal();
}
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}finally {
putLock.unlock();
}
if(c==0){//在put操作之前有因为队列空而阻塞的线程,进行释放
signalEmpty();
}
}
public E take() {
E e = null;
final ReentrantLock takeLock = this.takeLock;
final Condition empty = this.empty;
final AtomicInteger count = this.count;
int c = -1;
try {
takeLock.lockInterruptibly();
while (count.get() == 0) {
empty.await();
}
e = queue.get(0);
queue.remove(0);
c = count.getAndDecrement();
if(c>1) {//在出队列之前,队列中的元素个数大于1,即在出队之后元素个数大于0
empty.signal();
}
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
finally {
takeLock.unlock();
}
if(c == capacity) {//在进行get之前有线程因为队列满而阻塞
signalFull();
}
return e;
}
}
利用MyBlockQueue实现的生产者消费者模式:
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
public class Producer implements Runnable {
private AtomicInteger count = new AtomicInteger();
private int id;
//private BlockingQueue blockQueue;
private MyBlockQueue blockQueue;
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
int x = count.incrementAndGet();
String str = x + "";
try {
blockQueue.put(str);
System.out.println("Producer" + id + " produce: " + str);
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public Producer(MyBlockQueue blockQueue, int id) {
super();
this.blockQueue = blockQueue;
this.id = id;
}
}
public class Consumer implements Runnable {
private int id;
//private BlockingQueue blockQueue;
private MyBlockQueue blockQueue;
public Consumer(int id, MyBlockQueue blockQueue) {
super();
this.id = id;
this.blockQueue = blockQueue;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
try {
String str = blockQueue.take();
System.out.println("Consumer" + id + " consume:" + str);
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
@Test
public void testProducerAndConsumer() throws InterruptedException {
//BlockingQueue blockQueue = new LinkedBlockingQueue(5);
MyBlockQueue blockQueue = new MyBlockQueue();
for(int i=0; i<1; ++i) {
Thread t1 = new Thread(new Producer(blockQueue, i));
t1.start();
Thread t2 = new Thread(new Consumer(i, blockQueue));
t2.start();
t1.join();
t2.join();
}
}