并发容器和框架:
ConcurrentHashMap:
在激烈的并发环境下性能优异的原因在于使用了锁分段技术,ConcurrentHashMap里面维持一个Segment数组,每个Segment就相当于一个HashMap表,每个Segment继承了ReentrantLock。每个Segment维持一个tabel,即一个HashEntry数组,每个HashEntry又是一个链表结构。Segment数组长度和table数组长度都是2^N,这样方便使用散列散列算法定位:都与数组的长度减一再相“与”,但是相“与”的值不一样,定位Segment使用的值是hashcode再散列后的高位,而定位hashEntry直接使用再散列后的值。目的是避免俩次散列后的值一样。
ConcurrentHashMap的get操作先是定位到Segment上,然后调用Segment的get操作,get操作的高效之处在于整个get过程不需要加锁(除非得到的值是空才会加锁重读),为什么不需要加锁读,这是因为统计当前Segment大小的count字段和用于存储值的HashEntry的value都被定义成为了Volatile,这样根据Volatile的语义,总能读取到最新的值。
ConcurrentHashMap的put操作先是定位到Segment上,然后调用Segment的put操作,写操作要加锁,且需要经历俩个步骤:1:在插入数据前判断是否需要对Segment里面的HashEntry数组进行扩容,如果超过阈值,则对数组进行扩容。
2:在扩容的时候,先是创建一个容量是原来俩倍的数组,然后将原数组里面的元素再散列后插入新的数组里面,
ConcurrentHashMap里面的插入操作是在HashEntry链表的头结点插入的,删除操作需要复制之前的所有节点,因为HashEntry里面的next引用是final的。
ConcurrentHashMap的size操作:先尝试2次不锁住Segment的方式统计各个Segment的大小,如果统计过程中容器的大小发生了变化(通过modCount查看,每次put,remove,clean操作都会使modCount变量加1,且modCount变量时Volatile的),则进行加锁统计Segment的大小。
ConcurrentLinkedQueue:
入队列有俩步骤:1:将入队节点设置成当前队列尾节点的下一个节点,2:更新tail节点(tail节点并不总是指向尾节点,这样做是为了减少CAS更新tail节点的次数,提高入队效率),如果tail节点的next节点不为空,则将入队节点设置成tail节点,如果tail节点的next节点为空,则将入队节点设置成tail的next节点。
出队列:首先获取头结点的元素,然后判断头结点元素是否为空,如果为空,表示另外一个线程已经进行了一次出队操作取走该元素,如果不为空,则使用CAS的方式将头节点的引用设为空,如果成功,直接返回头结点的元素,否则,需要重新获取头结点。
ConcurrentLinkedQueue是无阻塞的并发队列。
阻塞的并发队列有如下几种:
ArrayBlockingQueue:
LinkedBlockingQueue:
PriorityBlockingQueue:
DelayQueue
SynchronousQueue
LinkedTransferQueue
LinkedBlockingQueue
他们都有4种处理方式:
1:add(e),remove(),element():抛出异常
2:offer(e),poll(),peek():返回特殊值
3:put(e),take():一直阻塞
4:offer(e,time,unit),poll(time,unit):超时退出