特点
1.无界队列(基于链表实现),从头部获取元素,在尾部插入元素,比基于数组的队列吞吐量更高
2.双锁队列的变种实现,一把写锁,一把读锁(这点和ArrayBlockingQueue有本质的区别)
3.默认队列的大小是Integer的最大值,如果添加速度大于读取速度的话,有可能造成内存溢出
4.因为是两把锁,所以元素的个数使用了一个原子类型的变量来维护(AtomicInteger)
主要方法
1.插入方法,put(E e),offer(E e, long timeout, TimeUnit unit),offer(E e)
1.1 put(E e)
队列如果元素满了,则阻塞当前线程;如果队列有空余,直接队尾插入,然后通知其他阻塞的插入线程
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();//检查元素是否为空
int c = -1;//数量标识
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock; //获取锁
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
while (count.get() == capacity) {//如果当前队列已经满了,阻塞当前线程,进行等待
notFull.await();
}
enqueue(node); //插入元素
c = count.getAndIncrement();//原子的增加数量
if (c + 1 < capacity)
notFull.signal();//如果数量小于队列的大小,说明有空间,可以通知等待的线程进行插入元素
} finally {
putLock.unlock();//释放锁
}
if (c == 0) //如果队列元素中,存在一个元素,则通知阻塞的消费端来读取元素,(如果消费元素速度远远大于插入速度,那么会有消费线程会被阻塞)
signalNotEmpty(); //该方法下面单独说明
}
1.2 offer(E e, 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 {
while (count.get() == capacity) {//如果队列元素已满,阻塞当前线程到一定时间
if (nanos <= 0)//如果阻塞时间已经到了,那么直接返回false
return false;
nanos = notFull.awaitNanos(nanos);
}
enqueue(new Node<E>(e));//插入元素
c = count.getAndIncrement();//增加元素数量
if (c + 1 < capacity)//如果队列没满,通知阻塞的插入线程
notFull.signal();
} finally {
putLock.unlock();//释放锁
}
if (c == 0)//如果队列元素中,存在一个元素,则通知阻塞的消费端来读取元素,(如果消费元素速度远远大于插入速度,那么会有消费线程会被阻塞)
signalNotEmpty();
return true;
}
1.3 offer(E e)
队列如果元素满了,则直接返回false,并不会阻塞线程;如果队列有空余,直接队尾插入,然后通知其他阻塞的插入线程
public boolean offer(E e) {
if (e == null) throw new NullPointerException();//检查元素是否为空
final AtomicInteger count = this.count;
if (count.get() == capacity)//如果队列已满,直接返回
return false;
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
putLock.lock();//获取锁
try {
if (count.get() < capacity) {//如果队列未满,插入元素
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)//如果队列未满,通知阻塞的线程
notFull.signal();
}
} finally {
putLock.unlock();//释放锁
}
if (c == 0) //如果队列元素中,存在一个元素,则通知阻塞的消费端来读取元素,(如果消费元素速度远远大于插入速度,那么会有消费线程会被阻塞)
signalNotEmpty();
return c >= 0; //如果小于0的话,代表没有插入成功
}
2.signalNotEmpty()
想要通知消费线程必须,先获取对应的锁,才能通知
private void signalNotEmpty() {
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();//获取锁
try {
notEmpty.signal(); //通知
} finally {
takeLock.unlock();//释放锁
}
}
3.获取元素的方法,take(),poll(long timeout, TimeUnit unit),poll(),peek()
3.1 take()
如果队列空了,阻塞当前线程;如果队列不为空,获取元素并删除,通知其他的阻塞线程进行获取元素;
public E take() throws InterruptedException {
E x;//记录获取的元素
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock; //获取锁
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {//如果队列已经空了,阻塞当前线程
notEmpty.await();
}
x = dequeue();//获取元素,并删除
c = count.getAndDecrement();//返回之前的数量,
if (c > 1) //队列还有元素,通知之前的阻塞线程
notEmpty.signal();
} finally {
takeLock.unlock();//释放锁
}
if (c == capacity)//如果原来队列已经满,那么现在取出一个,通知阻塞的插入线程
signalNotFull();
return x;
}
3.2 poll(long timeout, TimeUnit unit)
如果队列空了,在一定时间内阻塞当前线程;如果队列不为空,获取元素并删除,通知其他的阻塞线程进行获取元素;
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 {
while (count.get() == 0) {//如果队列为空,阻塞一定的时间
if (nanos <= 0)
return null;
nanos = notEmpty.awaitNanos(nanos);
}
x = dequeue();//获取元素并删除
c = count.getAndDecrement();//返回之前的数值
if (c > 1)//队列还有元素,通知之前的阻塞线程
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)//如果原来队列已经满,那么现在取出一个,通知阻塞的插入线程
signalNotFull();
return x;
}
3.3 poll()
如果队列空了,直接返回null;如果队列不为空,获取元素并删除,通知其他的阻塞线程进行获取元素;
public E poll() {
final AtomicInteger count = this.count;
if (count.get() == 0)//队列如果为空,直接返回null
return null;
E x = null;
int c = -1;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();//获取锁
try {
if (count.get() > 0) {//如果队列不为空,获取元素
x = dequeue();//获取元素并删除
c = count.getAndDecrement();
if (c > 1)//队列还有元素,通知之前的阻塞线程
notEmpty.signal();
}
} finally {
takeLock.unlock();//释放锁
}
if (c == capacity)//如果原来队列已经满,那么现在取出一个,通知阻塞的插入线程
signalNotFull();
return x;
}
3.4 peek()
如果队列空了,直接返回null;如果队列不为空,直接获取元素但并不删除;
public E peek() {
if (count.get() == 0) //如果队列为空,直接返回null
return null;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();//获取锁
try {
Node<E> first = head.next;
if (first == null)//如果头元素为空,直接返回为空
return null;
else
return first.item;//否则返回元素
} finally {
takeLock.unlock();//释放锁
}
}
4.signalNotFull()
想要通知生产线程必须,先获取对应的锁,才能通知
private void signalNotFull() {
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
notFull.signal();
} finally {
putLock.unlock();
}
}