前言

  1. 本文基于 jdk 8 编写。
  2. @author JellyfishMIX - github / blog.jellyfishmix.com
  3. LICENSE GPL-2.0

offer(E e) 方法[非阻塞]

offer 方法呈现的效果: offer(E e): 表示如果可能的话,将 e 添加到 BlockingQueue 里,即如果 BlockingQueue 可以容纳,则返回 true,否则返回 false。本方法不阻塞当前执行方法。

 /*** Current number of elements* 队列的当前元素个数*/private final AtomicInteger count = new AtomicInteger();/*** The capacity bound, or Integer.MAX_VALUE if none* 队列的容量上限*/private final int capacity;/*** Lock held by put, offer, etc* 写锁,put, offer 方法共用这把锁。可以理解为添加元素时用的写锁*/private final ReentrantLock putLock = new ReentrantLock();/** * Wait queue for waiting puts* 等待添加元素的线程*/private final Condition notFull = putLock.newCondition();/*** Linked list node class* 链表实现的队列,单个存储单元*/static class Node<E> {E item;/*** One of:* - the real successor Node* - this Node, meaning the successor is head.next* - null, meaning there is no successor (this is the last node)*/Node<E> next;Node(E x) { item = x; }}/*** Inserts the specified element at the tail of this queue if it is* possible to do so immediately without exceeding the queue's capacity,* returning {@code true} upon success and {@code false} if this queue* is full.* When using a capacity-restricted queue, this method is generally* preferable to method {@link BlockingQueue#add add}, which can fail to* insert an element only by throwing an exception.** @throws NullPointerException if the specified element is null*/public boolean offer(E e) {// 判空校验if (e == null) throw new NullPointerException();// 队列的当前元素个数// 1. 把成员变量 count 赋值给了局部变量 count 2. 局部变量用 final 修饰了final AtomicInteger count = this.count;// 如果当前队列的元素个数已达容量上限,则直接返回 falseif (count.get() == capacity)return false;// 表示添加元素后,队列的当前元素个数int c = -1;// node,新建一个存储单元Node<E> node = new Node<E>(e);// 当前的写锁,put, offer 方法共用这把锁。可以理解为添加元素时用的写锁final ReentrantLock putLock = this.putLock;// 加锁,进入线程安全区putLock.lock();try {// 在加锁后的线程安全区,判断队列中是否还有剩余容量if (count.get() < capacity) {// 入队列enqueue(node);// 队列容量 + 1c = count.getAndIncrement();// 如果添加元素后,队列仍然没有满,则唤醒一个等待添加元素的线程if (c + 1 < capacity)notFull.signal();}} finally {// 解锁putLock.unlock();}// 如果添加元素后,队列非空,则唤醒一个等待拿取(take)的线程if (c == 0)signalNotEmpty();// 表示是否添加成功return c >= 0;}

额外的注释:

// 队列的当前元素个数
final AtomicInteger count = this.count;

这里值得关注的问题有两处:

  1. 把成员变量 count 赋值给了局部变量 count
  2. 局部变量用 final 修饰了

回答 1. 这里把成员变量 count 赋值给了局部变量 count。访问成员变量,每次都需要读内存。但是局部变量可以放入 cpu 缓存,这样先把成员变量赋值给局部变量,读 cpu 缓存的时间会快于读内存。不过这种把成员变量先读成局部变量的方式,属于极端优化。正常写业务不用这么写,还可能增加心智负担。性能差异不大,不如考虑开发效率,降低心智负担。

回答 2.

我们先来看两张图的对比,可以发现对局部变量赋值的操作,加与不加 final,编译后的字节码是相同的。字节码里没有任何东西能体现出局部变量的 final 与否,Class 文件里除字节码(Code 属性)外的辅助数据结构也没有记录任何体现 final 的信息。既然带不带 final 的局部变量在编译到 Class 文件后都一样了,其访问效率必然一样高,JVM 不可能有办法知道什么局部变量原本是用 final 修饰来声明的。

(题外话: 有一个例外,编译常量时,final 常量折叠机制: JVM对于声明为final的局部变量(local var)做了哪些性能优化? - RednaxelaFX的回答 - 知乎)

猜测,截图中,jdk 源码里作者加 final 修饰,应该是为了避免自己手滑在后面改写了局部变量里最初读到的值,而加上 final 来让编译器(javac 之类)检查。

put(E e) 方法[阻塞]

put 方法呈现的效果:put(E e) 把 e 加到 BlockingQueue 里,如果 BlockingQueue 没有空间,则调用此方法的线程被阻塞直到BlockingQueue 里面有空间再继续。

 /*** Inserts the specified element at the tail of this queue, waiting if* necessary for space to become available.** @throws InterruptedException {@inheritDoc}* @throws NullPointerException {@inheritDoc}*/public void put(E e) throws InterruptedException {// 判空校验if (e == null) throw new NullPointerException();// Note: convention in all put/take/etc is to preset local var// holding count negative to indicate failure unless set.// 表示添加元素后,队列的当前元素个数int c = -1;// node,新建一个存储单元Node<E> node = new Node<E>(e);// 当前的写锁,put, offer 方法共用这把锁。可以理解为添加元素时用的写锁final ReentrantLock putLock = this.putLock;// 队列的当前元素个数final AtomicInteger count = this.count;// 后面 put 方法可能会阻塞住当前线程,因此加一个可中断的锁。进入线程安全区putLock.lockInterruptibly();try {/** Note that count is used in wait guard even though it is* not protected by lock. This works because count can* only decrease at this point (all other puts are shut* out by lock), and we (or some other waiting put) are* signalled if it ever changes from capacity. Similarly* for all other uses of count in other wait guards.*/// 如果当前队列的元素个数已达容量上限,则当前线程阻塞住,直到被信号唤醒或线程被中断while (count.get() == capacity) {notFull.await();}// 入队列enqueue(node);// 队列容量 + 1c = count.getAndIncrement();// 如果添加元素后,队列仍然没有满,则唤醒一个等待添加元素的线程if (c + 1 < capacity)notFull.signal();} finally {// 解锁putLock.unlock();}// 如果添加元素后,队列非空,则唤醒一个等待拿取(take)的线程if (c == 0)signalNotEmpty();}

poll() 方法

 /*** Lock held by take, poll, etc* 读锁,take, poll 方法共用这把锁。可以理解为拿取元素时用的读锁*/private final ReentrantLock takeLock = new ReentrantLock();

LinkedBlockingQueue 源码分析相关推荐

  1. 并发编程(九)—— Java 并发队列 BlockingQueue 实现之 LinkedBlockingQueue 源码分析...

    LinkedBlockingQueue 在看源码之前,通过查询API发现对LinkedBlockingQueue特点的简单介绍: 1.LinkedBlockingQueue是一个由链表实现的有界队列阻 ...

  2. java并发编程基础-ReentrantLock及LinkedBlockingQueue源码分析

    ReentrantLock是一个较为常用的锁对象.在上次分析的uil开源项目中也多次被用到,下面谈谈其概念和基本使用. 概念 一个可重入的互斥锁定 Lock,它具有与使用 synchronized 相 ...

  3. Spark RPC框架源码分析(二)RPC运行时序

    前情提要: Spark RPC框架源码分析(一)简述 一. Spark RPC概述 上一篇我们已经说明了Spark RPC框架的一个简单例子,Spark RPC相关的两个编程模型,Actor模型和Re ...

  4. Spark源码分析 – DAGScheduler

    DAGScheduler的架构其实非常简单, 1. eventQueue, 所有需要DAGScheduler处理的事情都需要往eventQueue中发送event 2. eventLoop Threa ...

  5. java 线程池 源码_java线程池源码分析

    我们在关闭线程池的时候会使用shutdown()和shutdownNow(),那么问题来了: 这两个方法又什么区别呢? 他们背后的原理是什么呢? 线程池中线程超过了coresize后会怎么操作呢? 为 ...

  6. Dubbo 源码分析 - 集群容错之 Cluster

    1.简介 为了避免单点故障,现在的应用至少会部署在两台服务器上.对于一些负载比较高的服务,会部署更多台服务器.这样,同一环境下的服务提供者数量会大于1.对于服务消费者来说,同一环境下出现了多个服务提供 ...

  7. ExecutorCompletionService 源码分析

    概要 在ExecutorService的submit方法中可以获取返回值,通过Future的get方法,但是这个Future类存在缺陷,Future接口调用get()方法取得处理后的返回结果时具有阻塞 ...

  8. 深入理解Spark 2.1 Core (十一):Shuffle Reduce 端的原理与源码分析

    我们曾经在<深入理解Spark 2.1 Core (一):RDD的原理与源码分析 >讲解过: 为了有效地实现容错,RDD提供了一种高度受限的共享内存,即RDD是只读的,并且只能通过其他RD ...

  9. 【JUC】JDK1.8源码分析之ConcurrentLinkedQueue(五)

    一.前言 接着前面的分析,接下来分析ConcurrentLinkedQueue,ConcurerntLinkedQueue一个基于链接节点的无界线程安全队列.此队列按照 FIFO(先进先出)原则对元素 ...

最新文章

  1. CTFshow 命令执行 web48
  2. 【最详细解析】1070 结绳 (25分)_18行代码AC
  3. BZOJ4568 : [Scoi2016]幸运数字
  4. 中国35位“大国工匠”榜单出炉!西工大、西电合计占半壁江山!清华仅1人!...
  5. 百度SEO万能网页操作编程者 v2.0
  6. Linux下能访问Nginx,本地无法访问
  7. 【hive】hive权限
  8. REST无状态风格的理解
  9. teched2004视频资料下载,又加了5段,全是开发类的
  10. 3S基础知识:MapInfo使用MapX开发实现若干小功能
  11. 图解十大机器学习算法
  12. java-合并两个有序链表
  13. Redis是什么?怎么用?
  14. vscode怎么设置动态背景
  15. 【长按图片识别】uniapp vue开发时,点击图片识别—实现转发、收藏、识别图片二维码
  16. 甘超波:NLP检定语言模式
  17. android百度日语输入法下载,百度日文输入法
  18. 基于SSM框架的图片分享及评价网站设计与实现毕业设计源码201524
  19. CSS Hank兼容浏览器的
  20. 抽奖滚动效果 python_Python使用Tkinter实现滚动抽奖器效果

热门文章

  1. [转载] 晓说——第30期:海上霸主航母(下)
  2. 转: include android instant app support 是什么?
  3. php调用itunes,使用PHP将SQL列从秒转换为iTunes兼容的播客时间格式
  4. 阿里巴巴张瑞谈面向未来的数据库架构
  5. LifeCycle 的使用和原理
  6. 17:Oriented R-CNN for Object Detection
  7. ORACLE压力测试工具orion
  8. Gateway网关简介及使用。Spring Cloud Alibaba---Gateway概述、简单示例。什么是Gataway网关?网关能干什么?Spring Cloud如何搭建一个网关。
  9. python中print()的作用是什么_在python中最常见的:print的真正用法
  10. PTA 2004年谷歌招聘题