Java基于链表的简单实现

1、阻塞队列的原理

阻塞队列与普通队列的区别在于:阻塞队列为空时,从队列中获取元素的操作将会被阻塞,当队列为满时,往队列里添加元素的操作会被阻塞。

试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。同样,试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程使队列重新变得空闲起来 

2、阻塞队列的简单实现

/** * 基于链表实现的一个阻塞队列 * * @author jade * */ public class BlockingQueue { private int capacity ; // 阻塞队列容量 private List queue = new LinkedList(); // 基于链表实现的一个阻塞队列 public BlockingQueue() { this(Integer.MAX_VALUE); } public BlockingQueue(int capacity) { this.capacity = capacity; } /** * 入队列 * * @param item * @throws InterruptedException */ public synchronized void enqueue(Object item) throws InterruptedException { while (this.queue.size() == this.capacity) { wait(); } if (this.queue.size() == 0) { notifyAll(); } this.queue.add(item); } /** * 出队列 * * @return * @throws InterruptedException */ public synchronized Object dequeue() throws InterruptedException { while (this.queue.size() == 0) { wait(); } if (this.queue.size() == this.capacity) { notifyAll(); } return this.queue.remove(0); } }

注意:

1)在enqueue和dequeue方法内部,只有队列的大小等于上限(capacity)或者下限(0)时,才调用notifyAll方法,小于时不调用。

如果队列的大小既不等于上限,也不等于下限,任何线程调用enqueue或者dequeue方法时,都不会阻塞,就不需要唤醒,都能够正常的往队列中添加或者移除元素。

2)enqueue和dequeue方法都加了synchronized ,在进入synchronized的时候获取锁,退出的时候释放锁。而如果没有synchronized,直接使用wait/notify时,无法确认哪个锁。 

3、LinkedBlockingQueue

Java.util.concurrent包下提供了若干个阻塞队列,其中LinkedBlockingQueue基于链表实现的一个阻塞队列,在创建LinkedBlockingQueue对象时如果不指定容量大小,则默认大小为Integer.MAX_VALUE。

首先看一下LinkedBlockingQueue类中的几个成员变量:

private static final long serialVersionUID = -6903933977591709194L; private final int capacity; private final AtomicInteger count = new AtomicInteger(); transient Node<E> head; private transient Node<E> last; private final ReentrantLock takeLock = new ReentrantLock(); private final Condition notEmpty = this.takeLock.newCondition(); private final ReentrantLock putLock = new ReentrantLock(); private final Condition notFull = this.putLock.newCondition();

可以看出,LinkedBlockingQueue中用来存储元素的实际上是一个链表,head和last分别表示链表的头节点和下一节点, capacity表示队列的容量。

takelock,putLock 是可重入锁,notEmpty和notFull是等待条件。

下面看一下LinkedBlockingQueue的构造器,构造器有三个重载版本:

public LinkedBlockingQueue() { this(Integer.MAX_VALUE); } public LinkedBlockingQueue(int paramInt) { if (paramInt <= 0) { throw new IllegalArgumentException(); } this.capacity = paramInt; this.last = (this.head = new Node(null)); } public LinkedBlockingQueue(Collection<? extends E> paramCollection) { this(Integer.MAX_VALUE); ReentrantLock localReentrantLock = this.putLock; localReentrantLock.lock(); try { int i = 0; Iterator localIterator = paramCollection.iterator(); while (localIterator.hasNext()) { Object localObject1 = localIterator.next(); if (localObject1 == null) { throw new NullPointerException(); } if (i == this.capacity) { throw new IllegalStateException("Queue full"); } enqueue(new Node(localObject1)); i++; } this.count.set(i); } finally { localReentrantLock.unlock(); } }

第一个构造器默认容量是Integer.MAX_VALUE,第二个构造器只有一个参数用来指定容量,第三个构造器可以指定一个集合进行初始化。

然后看它的两个关键方法的实现:put()和take():

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/1df088ba785fa66bc421276a3273a29d.html