⑴背景
阻塞队列常用于生产者消费者场景,生产者是向队列里添加元素的线程,消费者是向队列里取出元素的线程。阻塞队列的角色是供生产者存放元素,消费者取出元素的容器。
⑵阻塞队列
阻塞队列是一个支持两个附加操作的队列。(支持阻塞插入和移除方法)
①阻塞插入:当队列满时,队列会阻塞插入元素的线程,直到队列不满。
②阻塞移除:当队列空时,获取元素的线程会等待队列变为空。
1 /** The queued items */ 2 final Object[] items; 3 4 /** items index for next take, poll, peek or remove */ 5 int takeIndex; 6 7 /** items index for next put, offer, or add */ 8 int putIndex; 9 10 /** Number of elements in the queue */ 11 int count; 12 13 /* 14 * Concurrency control uses the classic two-condition algorithm 15 * found in any textbook. 16 */ 17 18 /** Main lock guarding all access */ 19 final ReentrantLock lock; 20 21 /** Condition for waiting takes */ 22 private final Condition notEmpty; 23 24 /** Condition for waiting puts */ 25 private final Condition notFull;
1 /** 2 * Creates an {@code ArrayBlockingQueue} with the given (fixed) 3 * capacity and the specified access policy. 4 * 5 * @param capacity the capacity of this queue 6 * @param fair if {@code true} then queue accesses for threads blocked 7 * on insertion or removal, are processed in FIFO order; 8 * if {@code false} the access order is unspecified. 9 * @throws IllegalArgumentException if {@code capacity < 1} 10 */ 11 public ArrayBlockingQueue(int capacity, boolean fair) {
//当队列容量小于等于0,会抛异常。当队列满时再继续插入,也会抛该异常 12 if (capacity <= 0) 13 throw new IllegalArgumentException();
//创建容量数组 14 this.items = new Object[capacity];
//创建可重入锁与阻塞条件 15 lock = new ReentrantLock(fair); 16 notEmpty = lock.newCondition(); 17 notFull = lock.newCondition(); 18 }
1 /** 2 * Inserts the specified element at the tail of this queue, waiting 3 * for space to become available if the queue is full. 4 * 5 * @throws InterruptedException {@inheritDoc} 6 * @throws NullPointerException {@inheritDoc} 7 */ 8 public void put(E e) throws InterruptedException { 9 checkNotNull(e); 10 final ReentrantLock lock = this.lock;
//lockInterruptibly()允许在等待时由其他线程的Thread.interrupt()方法来中断等待线程而直接返回,这时是不用获取锁的,而会抛出一个InterruptException。
//而ReentrantLock.lock()方法则不允许Thread.interrupt()中断,即使检测到了Thread.interruptted一样会继续尝试获取锁,失败则继续休眠。只是在最后获取锁成功之后在把当前线程置为interrupted状态。 11 lock.lockInterruptibly(); 12 try {
//当队列满时,阻塞插入队列的线程 13 while (count == items.length) 14 notFull.await();
//对列不满就入队列 15 enqueue(e); 16 } finally {
//最后必须释放锁资源 17 lock.unlock(); 18 } 19 }
1 public E take() throws InterruptedException { 2 final ReentrantLock lock = this.lock;
//加锁保证只有一个线程进入take方法 3 lock.lockInterruptibly(); 4 try {
//当队列为空时,阻塞取出元素的线程 5 while (count == 0) 6 notEmpty.await();
//当队列队列非空时,允许元素加入队列 7 return dequeue(); 8 } finally { 9 lock.unlock(); 10 } 11 }
1 /** 2 * Inserts element at current put position, advances, and signals. 3 * Call only when holding lock. 4 */ 5 private void enqueue(E x) { 6 // assert lock.getHoldCount() == 1; 7 // assert items[putIndex] == null; 8 final Object[] items = this.items; //确保所有元素只放进同一个数组中 9 items[putIndex] = x; 10 if (++putIndex == items.length) //当索引等于数组最大值时,索引置0 11 putIndex = 0; 12 count++; 13 notEmpty.signal(); //使用条件对象notEmpty通知,当调用take,poll,remove时,线程被阻塞,对列为空,当调用enqueue时,队列不为空了,使用signal函数进行通知 14 }
1 /** 2 * Extracts element at current take position, advances, and signals. 3 * Call only when holding lock. 4 */ 5 private E dequeue() { 6 // assert lock.getHoldCount() == 1; 7 // assert items[takeIndex] != null; 8 final Object[] items = this.items; 9 @SuppressWarnings("unchecked") 10 E x = (E) items[takeIndex]; 11 items[takeIndex] = null; 12 if (++takeIndex == items.length) 13 takeIndex = 0; 14 count--; 15 if (itrs != null) 16 itrs.elementDequeued(); 17 notFull.signal(); 18 return x; 19 }
该博客更详细,向博主学习,以后更深入学习后再来完善这篇博客。http://blog.youkuaiyun.com/x_i_y_u_e/article/details/52513038