此文已由作者赵计刚授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
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(); final E[] items = this.items; final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { try { while (count == items.length)//队列满了,一直阻塞在这里 /* * 一直等待条件notFull,即被其他线程唤醒 * (唤醒其实就是,有线程将一个元素出队了,然后调用notFull.signal()唤醒其他等待这个条件的线程,同时队列也不慢了) */ notFull.await(); } catch (InterruptedException ie) {//如果被中断 notFull.signal(); // 唤醒其他等待该条件(notFull,即入队)的线程 throw ie; } insert(e); } finally { lock.unlock(); } }
4、出队
4.1、public E poll()
原理:
如果没有元素,直接返回null;如果有元素,将队头元素置null,但是要注意队头是随时变化的,并非一直是items[0]。
使用方法:
abq.poll();
源代码:
/** * 出队 */ public E poll() { final ReentrantLock lock = this.lock; lock.lock(); try { if (count == 0)//如果没有元素,直接返回null,而非抛出异常 return null; E x = extract(); return x; } finally { lock.unlock(); } }
/** * 出队 */ private E extract() { final E[] items = this.items; E x = items[takeIndex];//获取出队元素 items[takeIndex] = null;//将出队元素位置置空 /* * 第一次出队的元素takeIndex==0,第二次出队的元素takeIndex==1 * (注意:这里出队之后,并没有将后面的数组元素向前移) */ takeIndex = inc(takeIndex); --count;//数组元素个数-1 notFull.signal();//数组已经不满了,唤醒其他等待notFull条件的线程 return x;//返回出队的元素 }
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 { long nanos = unit.toNanos(timeout);//将时间转换为纳秒 final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { for (;;) { if (count != 0) {//数组不空 E x = extract();//出队 return x; } if (nanos <= 0)//时间超时 return null; try { /* * 进行等待: * 在这个过程中可能发生三件事: * 1、被唤醒-->继续当前这个for(;;)循环 * 2、超时-->继续当前这个for(;;)循环 * 3、被中断-->之后直接执行catch部分的代码 */ nanos = notEmpty.awaitNanos(nanos); } catch (InterruptedException ie) { notEmpty.signal(); // propagate to non-interrupted thread throw ie; } } } finally { lock.unlock(); } }
4.3、public E take() throws InterruptedException
原理:
将队头元素出队,如果队列空了,一直阻塞,直到数组不为空或者线程被中断
使用方法:
try { abq.take(); } catch (InterruptedException e) { e.printStackTrace(); }
源代码:
/** * 将队头元素出队 * 如果队列空了,一直阻塞,直到数组不为空或者线程被中断 */ public E take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { try { while (count == 0)//如果数组为空,一直阻塞在这里 /* * 一直等待条件notEmpty,即被其他线程唤醒 * (唤醒其实就是,有线程将一个元素入队了,然后调用notEmpty.signal()唤醒其他等待这个条件的线程,同时队列也不空了) */ notEmpty.await(); } catch (InterruptedException ie) { notEmpty.signal(); // propagate to non-interrupted thread throw ie; } E x = extract(); return x; } finally { lock.unlock(); } }
总结:
1、具体入队与出队的原理图:这里只说一种情况,见下图,途中深色部分表示已有元素,浅色部分没有元素。
上面这种情况是怎么形成的呢?当队列满了,这时候,队头元素为items[0]出队了,就形成上边的这种情况。
假设现在又要出队了,则现在的队头元素是items[1],出队后就形成下面的情形。
出队后,对头元素就是items[2]了,假设现在有一个元素将要入队,根据inc方法,我们可以得知,他要插入到items[0]去,入队了形成下图:
以上就是整个入队出队的流程,inc方法上边已经给出,这里再贴一遍:
/** * i+1,数组下标+1 * 注意:这里这样写的原因。 */ final int inc(int i) { return (++i == items.length) ? 0 : i; }
2、三种入队对比:
offer(E e):如果队列没满,立即返回true; 如果队列满了,立即返回false-->不阻塞
put(E e):如果队列满了,一直阻塞,直到数组不满了或者线程被中断-->阻塞
offer(E e, long timeout, TimeUnit unit):在队尾插入一个元素,,如果数组已满,则进入等待,直到出现以下三种情况:-->阻塞
被唤醒
等待时间超时
当前线程被中断
3、三种出对对比:
poll():如果没有元素,直接返回null;如果有元素,出队
take():如果队列空了,一直阻塞,直到数组不为空或者线程被中断-->阻塞
poll(long timeout, TimeUnit unit):如果数组不空,出队;如果数组已空且已经超时,返回null;如果数组已空且时间未超时,则进入等待,直到出现以下三种情况:
被唤醒
等待时间超时
当前线程被中断
免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 深入解析SQLServer高可用镜像实现原理