ArrayBlockingQueue是用数组实现的线程安全的有界的阻塞队列。
具体逻辑其实就是BlockingQueue那一篇写的,只是用数组实现了队列,最重要的是去理解ReentrantLock和Condition.
线程安全是指,ArrayBlockingQueue内部通过“互斥锁”保护竞争资源,实现了多线程对竞争资源的互斥访问。而有界,则是指ArrayBlockingQueue对应的数组是有界限的。 阻塞队列,是指多线程访问竞争资源时,当竞争资源已被某线程获取时,其它要获取该资源的线程需要阻塞等待;而且,ArrayBlockingQueue是按 FIFO(先进先出)原则对元素进行排序,元素都是从尾部插入到队列,从头部开始返回。
ArrayBlockingQueue实现了BlockingQueue接口,继承了AbstractQueue,内部使用Object数组存储元素,容量capacity在构造器中给出,之后不会自动扩容.其中包含ReentrantLock和两个Condition变量,用来实现互斥访问的.ReentrantLock默认是非公平锁,详情看另一篇,构造器中可以指定锁是公平锁还是非公平锁.
使用两个变量putIndex,takeIndex表示入队和出队的位置.
具体实现:
1, offer;
public boolean offer(E var1) { checkNotNull(var1); //判null,拋NullPointerException ReentrantLock var2 = this.lock; var2.lock(); boolean var3; try { if(this.count == this.items.length) { //条件count==items.length时,队列已满,返回false; var3 = false; return var3; } this.enqueue(var1); //入队 var3 = true; } finally { var2.unlock(); } return var3; }主要是使用ReentrantLock的lock()方法,其他的逻辑就是队列满了返回false,不满入队,返回true.
2,enqueue
private void enqueue(E var1) { Object[] var2 = this.items; var2[this.putIndex] = var1; if(++this.putIndex == var2.length) { //因为队列由数组实现,当队列末尾没空间时,从数组开端入队,循环数组 this.putIndex = 0; } ++this.count; this.notEmpty.signal(); }
3,put
public void put(E var1) throws InterruptedException { checkNotNull(var1); ReentrantLock var2 = this.lock; var2.lockInterruptibly(); // lockInterruptibly()等待锁或保持锁的过程中被中断,抛出InterruptedException try { while(this.count == this.items.length) { this.notFull.await(); //这里是和offer不一样的地方,一直阻塞到有空间加入,当收到notFull.signal()时,便可以入队 } this.enqueue(var1); } finally { var2.unlock(); } }
4,poll
public E poll(long var1, TimeUnit var3) throws InterruptedException { //没有参数的poll方法,除了不等待,其他逻辑一样 long var4 = var3.toNanos(var1); ReentrantLock var6 = this.lock; var6.lockInterruptibly(); try { Object var7; while(this.count == 0) { if(var4 <= 0L) { var7 = null; return var7; } var4 = this.notEmpty.awaitNanos(var4); //等待var1时间 } var7 = this.dequeue(); return var7; } finally { var6.unlock(); } }5,removeAt
void removeAt(int var1) { Object[] var2 = this.items; if(var1 == this.takeIndex) { //如果正好等于出队下标,正常出队 var2[this.takeIndex] = null; if(++this.takeIndex == var2.length) { this.takeIndex = 0; } --this.count; if(this.itrs != null) { this.itrs.elementDequeued(); } } else { //将要删除的元素后面的元素往前移 int var3 = this.putIndex; int var4 = var1; while(true) { int var5 = var4 + 1; if(var5 == var2.length) { var5 = 0; } if(var5 == var3) { var2[var4] = null; this.putIndex = var4; --this.count; if(this.itrs != null) { this.itrs.removedAt(var1); } break; } var2[var4] = var2[var5]; var4 = var5; } } this.notFull.signal(); }6,take
public E take() throws InterruptedException { ReentrantLock var1 = this.lock; var1.lockInterruptibly(); Object var2; try { while(this.count == 0) { this.notEmpty.await(); //一直阻塞 } var2 = this.dequeue(); } finally { var1.unlock(); } return var2; }offer和poll对应,put和take对应
ArrayBlockingQueue使用示例:
1,生产者Producer:
public class Producer implements Runnable{ //容器 private final ArrayBlockingQueue<Bread> queue; Producer(ArrayBlockingQueue<Bread> queue){ this.queue = queue; } @Override public void run() { while(true){ produce(); } } private void produce(){ /** * put()方法是如果容器满了的话就会把当前线程挂起 * offer()方法是容器如果满的话就会返回false。 */ try { Bread bread = new Bread(); queue.put(bread); System.out.println("Producer:"+bread); } catch (InterruptedException e) { e.printStackTrace(); } } }2,消费者Consumer:
public class Consumer implements Runnable{ //容器 private final ArrayBlockingQueue<Bread> queue; Consumer(ArrayBlockingQueue<Bread> queue){ this.queue = queue; } @Override public void run() { while (true) { consume(); } } private void consume(){ /** * take()方法和put()方法是对应的,从中拿一个数据,如果拿不到线程挂起 * poll()方法和offer()方法是对应的,从中拿一个数据,如果没有直接返回null */ try { Bread bread = queue.take(); System.out.println("consumer:"+bread); } catch (InterruptedException e) { e.printStackTrace(); } } }3,生产的面包Bread:
public class Bread { private int size; public int getSize() { return size; } public void setSize(int size) { this.size = size; } @Override public String toString() { return "Bread{" + "size=" + size + '}'; } }
4,测试Test:
public class Test { public static void main(String[] args) { int capacity = 10; ArrayBlockingQueue<Bread> queue = new ArrayBlockingQueue<>(capacity); new Thread(new Producer(queue)).start(); new Thread(new Producer(queue)).start(); new Thread(new Consumer(queue)).start(); new Thread(new Consumer(queue)).start(); new Thread(new Consumer(queue)).start(); } }
具体逻辑其实就是BlockingQueue那一篇写的,只是用数组实现了队列,最重要的是去理解ReentrantLock和Condition.