一、java.util.concurrent Lock 实现阻塞队列
阻塞队列与普通队列的区别在于,当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞。试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。同样,试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程使队列重新变得空闲起来,如从队列中移除一个或者多个元素,或者完全清空队列,下图展示了如何通过阻塞队列来合作:
阻塞队列的定义:队列是一种先进先出的数据结构,如果队列为空,从队列中获取元素,消费者线程会阻塞,如果队列已经满了,生产者线程就会阻塞
这里使用lock锁和condition接口实现
代码如下:
package cn.erong.blockqueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 使用数组实现阻塞队列
* @author zhouy
*
*/
public class ArrayBlockingQueue<T> {
private Lock lock = new ReentrantLock();
private Object[] item = new Object[4];
private int startIndex,endIndex,count;//初始化时,默认值为0,count 定义为队列中元素的数目
private Condition notfull = lock.newCondition();
private Condition notempty = lock.newCondition();
public void add(T t){
lock.lock();
try{
System.out.println("存放值"+t);
while(count==item.length){
try {
System.out.println("队列已满,阻塞put线程");
notfull.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
item[startIndex++] = t;
count++;
if(startIndex==item.length){
startIndex=0;
}
notempty.signal();
}finally{
lock.unlock();
}
}
public T take(){
lock.lock();//获取锁不能写在try块中,如果发生异常,锁会被释放
try{
while(count==0){
try {
System.out.println("队列空了,阻塞take线程");
notempty.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
T t =(T)item[endIndex++];
System.out.println("取值"+t);
count--;
if(endIndex==item.length){
endIndex = 0;
}
notfull.signal();
return t;
}finally{
lock.unlock();
}
}
}
可以简单的测试一下
package cn.erong.blockqueue;
public class BlockTest {
public static void main(String[] args) {
//启动一个子线程,不断的生产元素
final ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<3;i++){
queue.add(i);
}
}
});
try {
thread.join();
thread.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
//主线程不断的获取,显然最后,主线程会阻塞
for(int i=0;i<15;i++){
queue.take();
}
}
}
结果如下:
队列空了,阻塞take线程
存放值0
存放值1
存放值2
取值0
取值1
取值2
队列空了,阻塞take线程
二、synchronized同步代码块实现阻塞队列
public class BlockingQueue {
private List queue = new LinkedList();
private int limit = 10;
public BlockingQueue(int limit){
this.limit = limit;
}
public synchronized void enqueue(Object item)
throws InterruptedException {
while(this.queue.size() == this.limit) { //注意要使用while循环,而不是if,因为下面的notifyAll可能把正在等待的入队线程给唤醒
wait();
}
if(this.queue.size() == 0) {
notifyAll();
}
this.queue.add(item);
}
public synchronized Object dequeue()
throws InterruptedException{
while(this.queue.size() == 0){
wait();
}
if(this.queue.size() == this.limit){
notifyAll();
}
return this.queue.remove(0);
}
}
必须注意到,在enqueue和dequeue方法内部,只有队列的大小等于上限(limit)或者下限(0)时,才调用notifyAll方法。如果队列的大小既不等于上限,也不等于下限,任何线程调用enqueue或者dequeue方法时,都不会阻塞,都能够正常的往队列中添加或者移除元素。
转自:https://segmentfault.com/a/1190000000373535
https://blog.csdn.net/ditto_zhou/article/details/77330733