`

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

0
1
分享到:
评论

相关推荐

    java并发工具包 java.util.concurrent中文版用户指南pdf

    1. java.util.concurrent - Java 并发工具包 2. 阻塞队列 BlockingQueue 3. 数组阻塞队列 ArrayBlockingQueue 4. 延迟队列 DelayQueue 5. 链阻塞队列 LinkedBlockingQueue 6. 具有优先级的阻塞队列 ...

    java.util.concurrent

    java.util.concurrent总体概览图。 收取资源分3分。需要的同学可以下载一下。 java.util.concurrent主要包括5个部分executor,colletions,locks,atomic,tools。 该图详细的列举了并发包下面的结构,包含所有接口和...

    Java并发工具包java.util.concurrent用户指南中英文对照阅读版.pdf

    java.util.concurrent - Java 并发工具包 2. 阻塞队列 BlockingQueue 3. 数组阻塞队列 ArrayBlockingQueue 4. 延迟队列 DelayQueue 5. 链阻塞队列 LinkedBlockingQueue 6. 具有优先级的阻塞队列 ...

    Java并发工具包java.util.concurrent用户指南中英文对照阅读版

    本资源包含两个 pdf 文档,一本根据 Jakob Jenkov 最新博客 (http://tutorials.jenkov.com/java-util-concurrent/index.html) 整理的 java_util_concurrent_user_guide_en.pdf,一个中文翻译的 java_util_concurrent...

    编写程序,使用两个线程,一个队列,其中一个线程从键盘读取数据,放入到队列中,直到读取的数据是字符串quit则结束

    1. 编写程序,使用两个线程,一个队列, 其中一个线程从键盘读取数据,放入到队列中,直到读取的数据是字符串quit则结束,线程的任务就是循环读取数据直到... (b) 必须使用java.util.concurrent.LinkedBlockingQueue.

    java并发包资源

    本资源包含两个 pdf 文档,一本根据 Jakob Jenkov 最新博客 (http://tutorials.jenkov.com/java-util-concurrent/index.html) 整理的 java_util_concurrent_user_guide_en.pdf,一个中文翻译的 java_util_concurrent...

    java并发工具包详解

    1. java.util.concurrent - Java 并发工具包 2. 阻塞队列 BlockingQueue 3. 数组阻塞队列 ArrayBlockingQueue 4. 延迟队列 DelayQueue 5. 链阻塞队列 LinkedBlockingQueue 6. 具有优先级的阻塞队列 ...

    sfbu:序列文件备份工具

    客户### 扫描给定目录,可选择包含一个简单的过滤器 import java.util.concurrent.LinkedBlockingQueue ;import java.util.* ;import java.util.Map.Entry ;import com.boleslaw.client.FileToHttpConsumer ;...

    Java并发包源码分析(JDK1.8)

    囊括了java.util.concurrent包中大部分类的源码分析,其中涉及automic包,locks包(AbstractQueuedSynchronizer、ReentrantLock、ReentrantReadWriteLock、LockSupport等),queue(ArrayBlockingQueue、...

    关于线程通信DatagramPacket的求助

    先放代码 ...import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.locks.ReentrantLock; import java.io.IOException; import java.net.*; public class UDPserver { publ

    stackoverflow-queue:问题代码

    import java.util.concurrent.LinkedBlockingQueue; import stackoverflow.item.ItemState; import stackoverflow.task.CreatingTask; import stackoverflow.task.FirstMovingTask; import stackoverflow.task....

    java核心知识点整理.pdf

    可达性分析............................................................................................................................................... 26 2.3.2. 2.3.3. 老年代 ........................

    JAVA核心知识点整理(有效)

    可达性分析............................................................................................................................................... 26 2.3.2. 2.3.3. 老年代 ........................

Global site tag (gtag.js) - Google Analytics