第6章 Java并发容器和框架
6.1 Concurrent Hash Map的实现原理与使用
6.1.1 为什么要使用Concurrent HashMap
HashTable同时排斥get与set方法
6.1.2 Concurrent HashMap的结构
6.1.3 初始化
略
6.1.4 定位Segment
定位到Segment通过Hash算法,这里通过再散列,让更高的位参与到了运算中。
6.1.5 Concurrent HashMap的操作
1>get操作
①get时不需要加锁,因为Entry<K,V>中的value加了volatile修饰,保证了可见性,典型的volatile代替锁。
②但在读到空值时会加锁重读
③在Segment中的hash算法与Concurrent中不同
2>put操作
①扩容:与HashMap不同
HashMap插入后判断是否需要扩容,Segment插入前判断,防止无效扩容
②如何扩容
创建一个两倍于原数组的数组,将原数组再散列后插入(效率低,内存占用有时变为三倍)
所以Concurrent HashMap不会整个扩容,只会对segment进行扩容。而且最好在可预知的情况下队HashMap进行初始size设置
3>size操作
通过两次不锁操作统计,并在统计中判断是否发烧了修改(通过put、remore与clean方法对modcount的加1),若发生过变化,则modcount加1
【JDK1.8之后不适用segment,通过CAS为null节点put;通过synchronized为有值节点put】
6.2 ConcurrentLinkedQueue
6.2.2 入队
1>入队过程
定位尾结点->入队节点变为尾结点的next
2>定位尾结点
tail或者tail.next为尾结点
3>CAS入队,若尾结点不为null,重新定位尾结点
4>HOPS设计意图
尾结点不一定是tail节点,这样可以减少更新tail节点,不过在查找尾结点时需要多读,但减少了写。
6.2.3 出队列
①获取头节点->将其中元素取出并置为null
②获取头节点->元素已为空->获取下个节点
6.3 Java中的阻塞队列
6.3.1 什么是阻塞队列
BlockingQueue支持插入/移除时阻塞
在插入/移除时可选方法:
①抛异常
②返回特殊值
③阻塞
④超时退出
6.3.2 Java里的阻塞队列
1>ArrayBlockingQueue
先进先出排序,但非公平,有界(可选公平)
2>LinkedBlockingQueue(不公平)
同上,链表实现,界限为Int.MAX_VALUE
3>PriorityBlockingQueue
按优先级排序,无界,最小堆(二叉小顶堆)
4>DelayQueue
支持延时获取元素的无界队列,用PriorityQueue实现。队列中的元素需要实现Delay接口,在创建元素时可指定需要多久才能获得元素
【应用场景:
1>缓存系统设计:在存入时指定过期时间,在线程中获取元素,获取到后表示已过期
2>定时任务调度:保存执行任务的任务与执行时间,获取到后开始执行,如:TimerQueue】
【如何使用:
①实现Delay接口,用time记录当前对象延迟时间,sequenceNumber标识元素在队列中的先后顺序
②实现getDelay方法,返回当前元素还需要延时多长时间。单位为纳秒,与time字段配合使用
③实现CompareTo方法,将time值最大的放在末尾】
【如何实现:
在获取元素时判断,没到延时时间则阻塞】
5>synchronousQueue
不存储元素,每一个put操作必须等待一个take操作,否则不能继续添加。默认非公平,可选公平,吞吐量高。
6>LinkedTransferQueue
①transfer方法:该方法添加元素时必会等到元素被消费才返回
②tryTransfer方法:尝试直接给消费者,不行则返回false,可设置超时
7>LinkedBlockingDeque
双向队列,减少竞争
6.3.3 阻塞队列的实现原理
使用通知模式,设置了两个condition,非空与非满时通知,由ReetrantLock获得,其中await由support.park()实现
【park阻塞线程返回的四种情况
①unpark执行或已执行
②线程被中断
③等带完time参数超时
④发生异常】
6.4 Fork/Join框架
6.4.1 什么是Fork/Join框架
把大任务fork成小任务(可多次fork)再join结果
6.4.2 工作窃取模式
某个线程从其他队列里窃取任务来执行
优点:利用线程优点进行并行计算,利用空闲线程
缺点:创建了多个线程与双端队列,消耗更多的资源
6.4.3 Fork/Join 框架的设计
①ForkJoinTask:创建事件
子类:RecursiveAction(无返回值)RecursiveTask(有返回值)
②ForkJoinPool:执行时间
6.4.4 使用Fork/Join框架
Task需实现ForkJoinTask接口并实现compare方法,方法中可完成任务何时分叉执行及何时真正运算,思想类似于递归
6.4.5 Fork/Join框架的异常处理
task.isCompletedAbnormally()判断是否抛出异常
getException()返回null或异常类型
6.4.6 Fork/Join框架的视线原理
主要并行在于compute方法中对于Task的fork与join
fork:当前线程不是ForkJoinWorker Thread,则说明是主Task,若是ForkJoinWorker Thread,则是子Task,加入queue