3.3、public void put(E e) throws InterruptedException

原理:

  • 在队尾插入一个元素,如果队列满了,一直阻塞,直到队列不满了或者线程被中断

使用方法:

try {
abq.put("hello1");
} catch (InterruptedException e) {
e.printStackTrace();
}


源代码:

/**
* 在队尾插一个元素
* 如果队列满了,一直阻塞,直到队列不满了或者线程被中断
*/
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
int c = -1;
final ReentrantLock putLock = this.putLock;//入队锁
final AtomicInteger count = this.count;//当前队列中的元素个数
putLock.lockInterruptibly();//加锁
try {
while (count.get() == capacity) {//如果队列满了
/*
* 加入notFull等待队列,直到队列元素不满了,
* 被其他线程使用notFull.signal()唤醒
*/
notFull.await();
}
enqueue(e);//入队
c = count.getAndIncrement();//入队数量+1
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
}


 

4、出队

4.1、public E poll()

原理:

  • 如果没有元素,直接返回null;如果有元素,出队

使用方法:

abq.poll();

源代码:

/**
* 出队:
* 1、如果没有元素,直接返回null
* 2、如果有元素,出队
*/
public E poll() {
final AtomicInteger count = this.count;// 获取元素数量
if (count.get() == 0)// 没有元素
return null;
E x = null;
int c = -1;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();// 获取出队锁
try {
if (count.get() > 0) {// 有元素
x = dequeue();// 出队
// 元素个数-1(注意:该方法是一个无限循环,直到减1成功为止,且返回旧值)
c = count.getAndDecrement();
if (c > 1)// 还有元素(如果旧值c==1的话,那么通过上边的操作之后,队列就空了)
notEmpty.signal();// 唤醒等待在notEmpty队列中的其中一条线程
}
} finally {
takeLock.unlock();// 释放出队锁
}
if (c == capacity)// c == capacity是怎么发生的?如果队列是一个满队列,注意:上边的c返回的是旧值
signalNotFull();
return x;
}
/**
* 从队列头部移除一个节点
*/
private E dequeue() {
Node<E> h = head;//获取头节点:x==null
Node<E> first = h.next;//将头节点的下一个节点赋值给first
h.next = h; // 将当前将要出队的节点置null(为了使其做head节点做准备)
head = first;//将当前将要出队的节点作为了头节点
E x = first.item;//获取出队节点的值
first.item = null;//将出队节点的值置空
return x;
}
private void signalNotFull() {
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
notFull.signal();
} finally {
putLock.unlock();
}
}


注意:出队逻辑如果不懂,查看最后总结部分的图

 

4.2、public E poll(long timeout, TimeUnit unit) throws InterruptedException

原理:

  • 从队头删除一个元素,如果队列不空,出队;如果队列已空且已经超时,返回null;如果队列已空且时间未超时,则进入等待,直到出现以下三种情况:
  • 被唤醒
  • 等待时间超时
  • 当前线程被中断

使用方法:

try {
abq.poll(1000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}


源代码:

/**
* 从队列头部删除一个元素,
* 如果队列不空,出队;
* 如果队列已空,判断时间是否超时,如果已经超时,返回null
* 如果队列已空且时间未超时,则进入等待,直到出现以下三种情况:
* 1、被唤醒
* 2、等待时间超时
* 3、当前线程被中断
*/
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;
/*
* 进行等待:
* 在这个过程中可能发生三件事:
* 1、被唤醒-->继续当前这个while循环
* 2、超时-->继续当前这个while循环
* 3、被中断-->抛出异常
*/
nanos = notEmpty.awaitNanos(nanos);
}
x = dequeue();//出队
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}