此文已由作者赵计刚授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
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; }
免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 图表设计-远不止“好看”这么简单
【推荐】 从细节处谈Android冷启动优化