qq空间刷赞网站推广手机浏览器直接打开网址
2026/4/16 22:43:01 网站建设 项目流程
qq空间刷赞网站推广,手机浏览器直接打开网址,建站工具 营销,app设计论文场景引入正如标题所言#xff0c;ArrayBlockingQueue是一个阻塞队列的数组实现#xff0c;如果要生动地描述阻塞队列的应用场景#xff0c;我想还是餐厅取餐、出餐的场景是最合适的#xff08;虽然我想这个场景已经被用烂了#xff09;。试想在学校的窗口取餐#xff0c;…场景引入正如标题所言ArrayBlockingQueue是一个阻塞队列的数组实现如果要生动地描述阻塞队列的应用场景我想还是餐厅取餐、出餐的场景是最合适的虽然我想这个场景已经被用烂了。试想在学校的窗口取餐一个窗口是预制好的烤肉拌饭另一个窗口则是现炒的小炒菜要如何选择先把目光移向烤肉拌饭因为出餐太快预制好的菜已经放满了窗台前出餐阿姨只能在窗口闲等等哪个着急的学生过来拿走一份饭她才能继续向上摆。再看向小炒菜由于味道美味很多学生都想吃但炒菜的出餐量有限学生没法立即享受到美味来晚的只能排队等待。哪怕学生再想吃窗台前都是空的没得拿只有出餐了才能拿。这就是阻塞队列的典型场景烤肉拌饭的窗台口已经摆满饭了阿姨只能阻塞等待有空了再放餐put操作。小炒菜的窗口太火爆了学生要想吃只能阻塞等待等轮到自己了在取餐take操作。API介绍放餐操作add、offer、putadd: 如果队列容量未满则插入指定元素; 成功时返回 true若当前无空位则抛出【IllegalStateException】offer: 如果队列容量未满则插入指定元素; 成功时返回 true若当前无空位则抛出 falseput: 如果队列容量未满则插入指定元素; 如果满了则阻塞等待有空位。如果等待时被打断则抛异常offer(long timeout, TimeUnit unit): 超时等待的put()。如果等着空了返回true如果超过timeout还是没有空位返回false取餐操作take、pollpoll: 如果队列容量不为空则取出队列头部并返回。如果为空返回nulltake: 如果队列容量不为空则取出队列头部并返回。如果为空则阻塞等待poll(long timeout, TimeUnit unit): 超时等待的take()如果队列容量不为空则取出队列头部并返回。如果为空则阻塞等待指定时间。如果超过时间还为空则返回null源码分析ArrayBlockingQueue的精髓之处就是阻塞等待的处理因此我将详细分析阻塞 take, put 相关的源码理解“阻塞”的灵魂所在。在此之前我们要先理解ArrayBlockingQueue的结构。从名字来看这个阻塞队列是以Array为基础的因此有界就很好理解。它是以数组为基础的数组的分配必须是连续的空间这段连续的空间就是阻塞队列的容量。同时我开篇也提到了这样的阻塞队列必须是线程安全的它底层使用了ReentrantLock来保证线程的安全。为什么还要有Condition等我们下文看源码就能知道了。enqueue、dequeue入队出队的底层//入队操作 private void enqueue(E e) { final Object[] items this.items; items[putIndex] e; if (putIndex items.length) putIndex 0; //如果当前元素入队后队列已满重置索引下一次再从队末进 count; notEmpty.signal();//如果元素加入到空队列中则唤醒阻塞等待的元素 }为什么不直接操作items反而要先定义一个局部变量final Object[] items this.items来接收它一、避免多次访问堆内存提升执行效率首先要明确 Java 的内存访问特性this.items是类的成员变量存储在堆内存中每次访问this.items都需要通过this引用堆地址去寻址相对耗时局部变量items存储在栈内存中栈内存的访问速度远快于堆内存而且局部变量的寻址是直接的无需额外间接引用。enqueue方法中多次用到了itemsitems[putIndex] e、putIndex items.length如果直接用this.items会多次触发堆内存寻址而先把this.items赋值给局部变量后续只需要访问栈上的局部变量即可减少了堆内存访问次数提升了执行效率。二、防止指令重排导致的可见性问题保证线程安全ArrayBlockingQueue是线程安全类items是共享成员变量虽然有ReentrantLock保证原子性但 Java 虚拟机的指令重排可能会导致潜在的可见性问题简单说final局部变量相当于给items数组拍了一张 “快照”确保整个enqueue方法执行期间使用的是同一个数组引用不会因为 JVM 优化而读取到错误的数组对象。//出队操作 private E dequeue() { final Object[] items this.items; SuppressWarnings(unchecked) E e (E) items[takeIndex]; //取出队列头部元素 items[takeIndex] null; //设为null if (takeIndex items.length) takeIndex 0; //与入队对应回到队末 count--; if (itrs ! null) itrs.elementDequeued(); notFull.signal(); //唤醒因队列满而阻塞等待的队列队列有空了 return e; }itrs是干什么的itrs是ArrayBlockingQueue用来管理当前所有活跃迭代器Iterator的集合目的是保证迭代器的弱一致性避免迭代过程中出现异常或错误的元素引用。当队列执行dequeue出队操作删除队首元素时会调用itrs.elementDequeued()核心目的是通知所有活跃迭代器“某个元素被删除了你们需要更新自己的状态避免遍历出错”。void elementDequeued() { // assert lock.isHeldByCurrentThread(); if (count 0) queueIsEmpty(); else if (takeIndex 0) takeIndexWrapped(); }queueIsEmpty()在本次出队后队列变空时重置迭代器状态nextIndex-1、remaining0标记迭代器遍历完毕避免无效遍历takeIndexWrapped()在本次出队后takeIndex循环回绕到数组开头时修正迭代器的nextWrapped状态保证迭代器能正确适配队列的循环结构put、take入队出队的实现public void put(E e) throws InterruptedException { Objects.requireNonNull(e); final ReentrantLock lock this.lock; lock.lockInterruptibly(); try { while (count items.length) notFull.await(); //如果队列满了阻塞等待 enqueue(e); } finally { lock.unlock(); } } public E take() throws InterruptedException { final ReentrantLock lock this.lock; lock.lockInterruptibly(); try { while (count 0) notEmpty.await(); //如果队列是空的阻塞等待 return dequeue(); } finally { lock.unlock(); } }可以看到ArrayBlockingQueue使用了ReentrantLock保证了入队出队的线程安全同时使用了条件变量实现了当队列为空/队列满了的情况下的阻塞等待。思考1. 可以看到take、put操作使用的都是同一把ReentrantLock这说明这两个操作是会互相阻塞的。怎么样才能让这两个操作独立起来2. 这个数组阻塞队列是强制有界的如果不确定队列大小的情况下该怎么处理这两个问题在 LinkedBlockingQueue 都给出了答复有兴趣的可以自行了解。我是沐浴露zz将持续更新有趣的技术欢迎互动交流

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询