阻塞隊列
隊列是一種只允許在標的前端進行刪除操作,而在表的藕斷進行插入操作的線性表。阻塞隊列和一班隊列的不同之處在於阻塞隊列是阻塞的,這裏的阻塞指的是操作隊列的線程的一種狀態。在阻塞隊列中,線程阻塞有如下兩種情況。
消費者阻塞
在隊列爲空時,消費者端的線程都會被自動阻塞(掛起),直到有數據放入隊列,消費者線程會被自動喚醒並消費數據。
生產者阻塞
在隊列已滿且沒有可用空間時,生產者端的線程都會被自動阻塞(掛起),直到隊列中有空的位置騰出,線程會被自動喚醒並生產數據。
阻塞隊列的主要操作
阻塞隊列的主要操作 有插入操作和移除操作。插入操作有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();//釋放鎖 } }