標籤:style blog color java 使用 io ar 問題
對於BlockingQueue的具體實現,主要關注的有兩點:安全執行緒的實現和阻塞操作的實現。所以分析ArrayBlockingQueue也是基於這兩點。
對於安全執行緒來說,所有的添加元素的方法和拿走元素的方法都會涉及到,我們通過分析offer方法和poll()方法就能看出安全執行緒是如何?的。
首先來看offer方法
public boolean offer(E e) { checkNotNull(e); final ReentrantLock lock = this.lock; lock.lock(); try { if (count == items.length) return false; else { insert(e); return true; } } finally { lock.unlock(); } }
通過代碼可以看出是通過採用Lock的方式來擷取鎖,然後再進行插入操作,最後再釋放鎖。
因此對於poll方法來說實現的方法肯定也是大同小異
public E poll() { final ReentrantLock lock = this.lock; lock.lock(); try { return (count == 0) ? null : extract(); } finally { lock.unlock(); } }
說過了安全執行緒的實現,接下來說說阻塞是如何?的。如果各位知道Object的wait/notify的話就很好理解了。這裡涉及到一個介面叫java.util.concurrent.locks.Condition。
Condition擁有類似的操作:await/signal。Condition和一個Lock相關,由Lock的newCondition來建立。只有當前線程擷取了這把鎖,才能調用Condition的await方法來等待通知,否則會拋出異常。
下面來看看put方法就會明白如何使用一個Condition了
notFull = lock.newCondition();
public void put(E e) throws InterruptedException { checkNotNull(e); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { while (count == items.length) notFull.await(); insert(e); } finally { lock.unlock(); } }
實現阻塞的關鍵就是就是這個notFull的Condition,當隊列已滿,await方法會阻塞當前線程,並且釋放Lock,等待其他線程調用notFull的signal來喚醒這個阻塞的線程。那麼這個操作必然會在拿走元素的操作中出現,這樣一旦有元素被拿走,阻塞的線程就會被喚醒。
這裡有個問題,發出signal的線程肯定擁有這把鎖的,因此await方法所在的線程肯定是拿不到這把鎖的,await方法不能立刻返回,需要嘗試擷取鎖直到擁有了鎖才可以從await方法中返回。
這就是阻塞的實現原理,也是所謂的線程同步。
同樣對於take方法會有一個notEmpty的Condition。
public E take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { while (count == 0) notEmpty.await(); return extract(); } finally { lock.unlock(); } }
需要注意的是這裡返回隊列長度的時候也是需要鎖的
public E take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { while (count == 0) notEmpty.await(); return extract(); } finally { lock.unlock(); } }
ArrayBlockingQueue的實現相對簡單,只需要一把鎖就可以搞定,下一篇關於LinkedBlockingQueue則會複雜不少,需要用到兩把鎖。