// 容量大小private final int capacity;// 元素个数,因为有2个锁,存在竞态条件,使用AtomicIntegerprivate final AtomicInteger count = new AtomicInteger(0);// 头结点private transient Node<E> head;// 尾节点private transient Node<E> last;// 拿锁private final ReentrantLock takeLock = new ReentrantLock();// 拿锁的条件对象private final Condition notEmpty = takeLock.newCondition();// 放锁private final ReentrantLock putLock = new ReentrantLock();// 放锁的条件对象private final Condition notFull = putLock.newCondition();[url=][/url]
ArrayBlockingQueue只有1个锁,添加数据和删除数据的时候只能有1个被执行,不允许并行执行。
而LinkedBlockingQueue有2个锁,放锁和拿锁,添加数据和删除数据是可以并行进行的,当然添加数据和删除数据的时候只能有1个线程各自执行。
数据的添加LinkedBlockingQueue有不同的几个数据添加方法,add、offer、put方法。
add方法内部调用offer方法:
[url=][/url]
public boolean offer(E e) { if (e == null) throw new NullPointerException(); // 不允许空元素 final AtomicInteger count = this.count; if (count.get() == capacity) // 如果容量满了,返回false return false; int c = -1; Node<E> node = new Node(e); // 容量没满,以新元素构造节点 final ReentrantLock putLock = this.putLock; putLock.lock(); // 放锁加锁,保证调用offer方法的时候只有1个线程 try { if (count.get() < capacity) { // 再次判断容量是否已满,因为可能拿锁在进行消费数据,没满的话继续执行 enqueue(node); // 节点添加到链表尾部 c = count.getAndIncrement(); // 元素个数+1 if (c + 1 < capacity) // 如果容量还没满 notFull.signal(); // 在放锁的条件对象notFull上唤醒正在等待的线程,表示可以再次往队列里面加数据了,队列还没满 } } finally { putLock.unlock(); // 释放放锁,让其他线程可以调用offer方法 } if (c == 0) // 由于存在放锁和拿锁,这里可能拿锁一直在消费数据,count会变化。这里的if条件表示如果队列中还有1条数据 signalNotEmpty(); // 在拿锁的条件对象notEmpty上唤醒正在等待的1个线程,表示队列里还有1条数据,可以进行消费 return c >= 0; // 添加成功返回true,否则返回false}[url=][/url]
put方法:
[url=][/url]
public void put(E e) throws InterruptedException { if (e == null) throw new NullPointerException(); // 不允许空元素 int c = -1; Node<E> node = new Node(e); // 以新元素构造节点 final ReentrantLock putLock = this.putLock; final AtomicInteger count = this.count; putLock.lockInterruptibly(); // 放锁加锁,保证调用put方法的时候只有1个线程 try { while (count.get() == capacity) { // 如果容量满了 notFull.await(); // 阻塞并挂起当前线程 } enqueue(node); // 节点添加到链表尾部 c = count.getAndIncrement(); // 元素个数+1 if (c + 1 < capacity) // 如果容量还没满 notFull.signal(); // 在放锁的条件对象notFull上唤醒正在等待的线程,表示可以再次往队列里面加数据了,队列还没满 } finally { putLock.unlock(); // 释放放锁,让其他线程可以调用put方法 } if (c == 0) // 由于存在放锁和拿锁,这里可能拿锁一直在消费数据,count会变化。这里的if条件表示如果队列中还有1条数据 signalNotEmpty(); // 在拿锁的条件对象notEmpty上唤醒正在等待的1个线程,表示队列里还有1条数据,可以进行消费}