阻塞队列
队列是一种只允许在标的前端进行删除操作,而在表的藕断进行插入操作的线性表。阻塞队列和一班队列的不同之处在于阻塞队列是阻塞的,这里的阻塞指的是操作队列的线程的一种状态。在阻塞队列中,线程阻塞有如下两种情况。
消费者阻塞
在队列为空时,消费者端的线程都会被自动阻塞(挂起),直到有数据放入队列,消费者线程会被自动唤醒并消费数据。
生产者阻塞
在队列已满且没有可用空间时,生产者端的线程都会被自动阻塞(挂起),直到队列中有空的位置腾出,线程会被自动唤醒并生产数据。
阻塞队列的主要操作
阻塞队列的主要操作 有插入操作和移除操作。插入操作有add(e),offer(e),put(e),offer(e,time,unit),移除操作有remove(),pull()
插入操作
-
public abstract boolean add(E paramE): 将指定的元素插入队列中,在成功时返回true,如果当前没有可用的空间,则抛出IlegalStateException。如果元素师null,则抛出NullPointException异常。JDK源码的实现如下:
public boolean add(E e){ //添加一个数据,如果成功,则返回true if(offer(e)) return true; //如果失败返回异常 else throw new IllegalStateException("Queue full"); }
-
public abstract boolean offer(E paramE): 将指定的元素插入队列中,在成功时返回true,如果当前没有可用的空间,则返回false。JDK源码的实现如下:
public boolean offer(E e){ checkNotNull(e); final ReentranLock lock = this.lock; lock.lock();//获取锁try try{ if(count == items.length) //如果队列满了,则返回false return false; else{ enqueue(e);//如果队列有空间,则将元素加入队列中 return true; } }finally{ lock.unlock(); } }
-
offer(E o,long timeout, TimeUnit unit):将指定的元素插入队列中,可以设定等待的时间,如果在设定的等待时间内仍不能像队列中加入元素,则返回false。
-
public abstract void put(E paramE)throws InterruptedException:将指定的元素插入队列中,如果对烈烈已经满了,则阻塞,等待可用的队列空间的释放,直到有可用的队列空间释放且插入成功为止,JDK源码的实现如下
public void put(E e)throws InterruptedException { checkNotNull(e); final ReentrantLock lock =this.lock; lock.lockInterruptibly(); try{ while(count == items.length)//阻塞等待可用空间的释放 notFull.await(); enqueue(e);//将元素加入队列中 }finally{ lock.lock(); } }
获取数据操作
- poll();取走队列队首的对象,如果取不到数据,则返回null。JDK源码的实现如下:
public E poll(){ final ReenTranLock lock= this.lock; lock.lock(); try{ //如果获取不到数据count == 0 ,则返回null return (count ==0 )? null : dequeue(); }finally{ lock.unlock();//释放锁 } }
- poll(long timeout, TimeUnit unit):取走队列队首的对象,如果在指定的时间内队列有数据可取,则返回队列中的数据,否则等待一定时间,在等待超时并且没有数据可取时,返回null。
- take():取走队列队首的对象,入股哦队列为空,则进入阻塞状态等待,直到队列有心的数据被加入,在集市去除新加入的数据,JDK源码的实现如下:
public E take() throws InterruptedException{ final ReentrantLock lock = this.lock; lock.lockInterruptibly();//获取独占锁 try{ //如果队列为空,则进入阻塞状态,直到能取到数据为止 while(count==0){ notEmpty.await(); return dequeue();//取出元素} finally{ lock.lock(); } } } }
- drainTo(Collection collection): 一次性从队列中批量获取所有可用的数据对象,同事可以指定获取数据的个数,通过该方法可以提升获取数据的效率,避免多次频繁操作引起的队列锁定。JDK源码的实现如下:
public int drainTo(Collection<? super E> c,int maxElemenets){ checkNotNull(c); if(c == this) throw new IllegalArgumentException(); if(maxElemenet <= 0) return 0; final Object[] items = this.items; final ReentrantLock lock = this.lock; // 获取锁操作lock.loc看(); try{ int n =Math.min(maxElemnent, count)//获取队列中指定个数的元素 int take = takeIndex; int i = 0; try{ while(i < n){ @SuppressWarnings("unchecked") E x = (E) items[take]; c.add(x); items[take]=null; if (++take == items.length) take =0; i++; } return n; }finally{ //Restore inveriants even if c.add() threw// 如果在drainTo过程中有新的数据加入,则处理该数据 if(i>0){ count -=i; takeIndex = take; if( itrs !=null){ if(count ==0) itrs.queueIsEmpty(); else if( i > take) itrs.takeIndexWrapped(); } for(;i>0&& lock.hasWaiters(notFull);i--) notFull.signal();//唤醒等待的生产者线程 } } }finally{ lock.unlock();//释放锁 } }