Java并发编程的艺术读书笔记(第六章)

第六章-Java并发容器和框架

ConcurrentHashMap的实现原理与使用

ConcurrentHashMap源码分析(1.8)
上面是源码博客,这里记录一些重点。

ConcurrentHashMap跟HashMap,HashTable的对比

效率 HashMap > ConcurrentHashMap > HashTable, 在此之上 HashMap不能确保多线程环境下的安全,由此可见HashTable也是已经完全被ConcurrentHashMap代替。因为 HashTable在并发情况下是锁住了整个对象 而ConcurrnetHashMap在 1.7 使用了分段锁,1.8使用了节点锁支持并发读写,1.8引入红黑树等。效率也是逐渐上升,当然在1.8源码中还保留着使用分段锁的实现,也只是为了兼容性考虑。

ConcurrentHashMap一些基本的概念
  • 1 ConcurrentHashMap通过Node保存map中的健值对,如果健对hash值相同则每个数组的位置通过链表或红黑树保存。

    • 当链表长度大于8时,链表将会转为红黑树。
  • 2 默认长度为16,如果指定了长度,初始化和扩容时会扩容为2^n次方,目的是方便扩容。
    原理来自美团技术文章:

    下面我们讲解下JDK1.8做了哪些优化。经过观测可以发现,我们使用的是2次幂的扩展(指长度扩为原来2倍),所以,元素的位置要么是在原位置,要么是在原位置再移动2次幂的位置。看下图可以明白这句话的意思,n为table的长度,图(a)表示扩容前的key1和key2两种key确定索引位置的示例,图(b)表示扩容后key1和key2两种key确定索引位置的示例,其中hash1是key1对应的哈希与高位运算结果。

    元素在重新计算hash之后,因为n变为2倍,那么n-1的mask范围在高位多1bit(红色),因此新的index就会发生这样的变化:

    因此,我们在扩充HashMap的时候,不需要像JDK1.7的实现那样重新计算hash,只需要看看原来的hash值新增的那个bit是1还是0就好了,是0的话索引没变,是1的话索引变成“原索引+oldCap”,可以看看下图为16扩充为32的resize示意图:

    这个设计确实非常的巧妙,既省去了重新计算hash值的时间,而且同时,由于新增的1bit是0还是1可以认为是随机的,因此resize的过程,均匀的把之前的冲突的节点分散到新的bucket了。这一块就是JDK1.8新增的优化点。有一点注意区别,JDK1.7中rehash的时候,旧链表迁移新链表的时候,如果在新表的数组索引位置相同,则链表元素会倒置,但是从上图可以看出,JDK1.8不会倒置。

  • 3 ConcurrentHashMap 在初始化的时候只会初始化sizeCtl,该变量用来判断是否需要扩容,和HashMap一样 sizeCtl = 总长度 * 负载因子(0.75),当达到这个值则会扩容。

    • 还有一种特殊情况没有达到上述条件也会扩容。就是当如果数组中的链表长度达到8个以上时,且数组长度小于64时则会进行一次扩容,当然如果此时数组长度大于64时则会转化成红黑树。
  • 4 初始化数组是在第一次插入元素时。

关于同步的细节查看博客即可。

ConcurrentLinkedQueue

Java中的阻塞队列

熟悉阻塞队列是重点

方法/处理方式抛出异常返回特殊值一直阻塞超时退出
插入方法add(e)offer(e)put(e)offer(e,time,until)
移除方法remove(e)poll(e)take(e)poll(time,until)
检查方法element()peek()

记忆:有返回值的都带o,阻塞的都带t

在下面的无界阻塞队列,不可能出现满的情况,所以put永远不会阻塞,offer永远返回true

Java里的阻塞队列

有界

  • ArrayBlockingQueue
    • 由数组结构实现
    • FIFO
    • 可使用通过重写构造实现公平访问队列
  • LinkedBlockingQueue
    • 由链表实现
    • FIFO
    • 默认最大长度 Integer.MAX_VALUE

无界

  • PriorityBlockingQueue
    • 支持优先级排序
    • 不能保证同优先级顺序
  • DelayQueue
    • 由优先级队列PriorityQueue实现
    • 在创建元素时可以指定多久才能从队列中获取当前元素
      • 缓存设计:保存元素有效期,使用线程循环查询队列,能取到时表示缓存有效期到了
      • 定时任务:队列保存当天需要执行的任务和执行时间,能取到时就执行任务
    • 可以实现compareTo方法指定元素到顺序。例如让延时最长放在队列的末尾
  • LinkedTransferQueue
    • 由链表实现
    • 与其他队列相比多了几个法方
      • transfer:如果取元素时消费者正在等待接收元素,transfer会将元素直接传递给消费者,如果没有消费者等待接收,则transfer会将元素放在队列tail节点,直到被消费者消费了才会返回。
      • tryTransfer:试探生产者传入的元素是否能直接传给消费者,不等待,直接返回结果。
      • tryTransfer(E e,long timeout,TimeUnit unit),等待元素传递给消费者,如果超时则返回结果。

特殊

  • SynchronousQueue
    • 不存储元素
    • 一个put必须等待一个take
  • LinkedBlockingDeque
    • 由链表组成的双向队列
    • 可从队列头尾插入删除(可以运用在”工作窃取模式中“)

Fork/Join框架

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值