java.util.concurrent 之LinkedBlockingQueue源碼分析

   LinkedBlockingQueue類

一個基於已鏈接節點的、範圍任意的 blocking queue。此隊列按 FIFO(先進先出)排序元素。隊列的頭部 是在隊列中時間最長的元素。隊列的尾部 是在隊列中時間最短的元素。新元素插入到隊列的尾部,並且隊列獲取操作會獲得位於隊列頭部的元素。鏈接隊列的吞吐量通常要高於基於數組的隊列,但是在大多數併發應用程序中,其可預知的性能要低。 

可選的容量範圍構造方法參數作爲防止隊列過度擴展的一種方法。如果未指定容量,則它等於 Integer.MAX_VALUE。除非插入節點會使隊列超出容量,否則每次插入後會動態地創建鏈接節點。 

 

   這個類的常見用法,應該用過的都應該知道了,就不再舉例了,直接進入源碼的分析。在分析其他方法前,先看下類變量

	/** The capacity bound, or Integer.MAX_VALUE if none */
	private final int capacity;

	/** Current number of elements */
	private final AtomicInteger count = new AtomicInteger(0);

	/** Head of linked list */
	private transient Node<E> head;

	/** Tail of linked list */
	private transient Node<E> last;

	/** Lock held by take, poll, etc */
	private final ReentrantLock takeLock = new ReentrantLock();

	/** Wait queue for waiting takes */
	private final Condition notEmpty = takeLock.newCondition();

	/** Lock held by put, offer, etc */
	private final ReentrantLock putLock = new ReentrantLock();

	/** Wait queue for waiting puts */
	private final Condition notFull = putLock.newCondition();

 

其中有兩個鎖和兩個條件變量是最重要的,決定了下面的方法實現。。阻塞的效果是靠這些來實現的。具體看下面的方法分析。

 

new 創建對象

	/**
	 * Creates a <tt>LinkedBlockingQueue</tt> with a capacity of
	 * {@link Integer#MAX_VALUE}. 從類名可以看出它主要是用鏈表來保存數據的。。下面的分析也可以看出來確實如此
	 * 
	 */
	public LinkedBlockingQueue() {
		// 這裏可以看出這個類是有容量限制的,默認是最大容量 int.max
		this(Integer.MAX_VALUE);
	}

	public LinkedBlockingQueue(int capacity) {
		if (capacity <= 0)
			throw new IllegalArgumentException();
		this.capacity = capacity;
		// 這裏可以看出鏈表的頭和尾默認都是null節點
		last = head = new Node<E>(null);
	}

	/**
	 * Linked list node class 這裏是鏈表的節點實現了
	 */
	static class Node<E> {
		/** The item, volatile to ensure barrier separating write and read */
		// 這裏的volatile變量確保了多線程中內存的一致性
		volatile E item;
		Node<E> next;

		Node(E x) {
			item = x;
		}
	}

 

put方法

	public void put(E e) throws InterruptedException {
		if (e == null)
			throw new NullPointerException();
		// Note: convention in all put/take/etc is to preset
		// local var holding count negative to indicate failure unless set.
		int c = -1;
		final ReentrantLock putLock = this.putLock;
		final AtomicInteger count = this.count;
		// put 加鎖
		putLock.lockInterruptibly();
		try {
			/*
			 * Note that count is used in wait guard even though it is not
			 * protected by lock. This works because count can only decrease at
			 * this point (all other puts are shut out by lock), and we (or some
			 * other waiting put) are signalled if it ever changes from
			 * capacity. Similarly for all other uses of count in other wait
			 * guards.
			 */
			try {
				// 判斷是否滿了,如果滿了則notFull 線程等待
				while (count.get() == capacity)
					notFull.await();
			} catch (InterruptedException ie) {
				notFull.signal(); // propagate to a non-interrupted thread
				throw ie;
			}
			// 插入鏈表
			insert(e);
			// 更新容量
			c = count.getAndIncrement();
			// 沒滿則喚醒notFull線程
			if (c + 1 < capacity)
				notFull.signal();
		} finally {
			putLock.unlock();
		}
		// 當c==0代表容量剛從空轉爲非空狀態,則喚醒非空線程
		if (c == 0)
			signalNotEmpty();
	}

	/**
	 * Signals a waiting take. Called only from put/offer (which do not
	 * otherwise ordinarily lock takeLock.)
	 */
	private void signalNotEmpty() {
		final ReentrantLock takeLock = this.takeLock;
		takeLock.lock();
		try {
			// 喚醒非空線程
			notEmpty.signal();
		} finally {
			takeLock.unlock();
		}
	}

 

take 方法

	// 這裏的加鎖方式剛好和put 方法相反。就不多說了。
	public E take() throws InterruptedException {
		E x;
		int c = -1;
		final AtomicInteger count = this.count;
		final ReentrantLock takeLock = this.takeLock;
		takeLock.lockInterruptibly();
		try {
			try {
				while (count.get() == 0)
					notEmpty.await();
			} catch (InterruptedException ie) {
				notEmpty.signal(); // propagate to a non-interrupted thread
				throw ie;
			}

			x = extract();
			c = count.getAndDecrement();
			if (c > 1)
				notEmpty.signal();
		} finally {
			takeLock.unlock();
		}
		if (c == capacity)
			signalNotFull();
		return x;
	}

 

poll 方法

// 這個其實和poll()無參數的方法類似,只是多了for循環的一個計數的功能。poll()就不分析了。
	public E poll(long timeout, TimeUnit unit) throws InterruptedException {
		E x = null;
		int c = -1;
		long nanos = unit.toNanos(timeout);
		final AtomicInteger count = this.count;
		final ReentrantLock takeLock = this.takeLock;
		takeLock.lockInterruptibly();
		try {
			for (;;) {// 這個是爲了計時而做的循環
				if (count.get() > 0) {
					x = extract();
					c = count.getAndDecrement();
					if (c > 1)
						notEmpty.signal();
					break;
				}
				if (nanos <= 0)
					return null;
				try {
					// awaitNanos 方法返回的是nanosTimeout
					// 值減去花費在等待此方法的返回結果的時間的估算,相當於就是剩餘時間了。
					nanos = notEmpty.awaitNanos(nanos);
				} catch (InterruptedException ie) {
					notEmpty.signal(); // propagate to a non-interrupted thread
					throw ie;
				}
			}
		} finally {
			takeLock.unlock();
		}
		if (c == capacity)
			signalNotFull();
		return x;
	}

 

offer方法

// 這裏和poll(long timeout, TimeUnit unit)方法的鎖的實現相反。。也不多說了。
	public boolean offer(E e, long timeout, TimeUnit unit)
			throws InterruptedException {

		if (e == null)
			throw new NullPointerException();
		long nanos = unit.toNanos(timeout);
		int c = -1;
		final ReentrantLock putLock = this.putLock;
		final AtomicInteger count = this.count;
		putLock.lockInterruptibly();
		try {
			for (;;) {
				if (count.get() < capacity) {
					insert(e);
					c = count.getAndIncrement();
					if (c + 1 < capacity)
						notFull.signal();
					break;
				}
				if (nanos <= 0)
					return false;
				try {
					nanos = notFull.awaitNanos(nanos);
				} catch (InterruptedException ie) {
					notFull.signal(); // propagate to a non-interrupted thread
					throw ie;
				}
			}
		} finally {
			putLock.unlock();
		}
		if (c == 0)
			signalNotEmpty();
		return true;
	}

 

可以看出LinkedBlockingQueue 類的實現比較簡單,也很容易理解,靈活應用了條件變量(Condition),減少了鎖的競爭。。   by zhxing

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章