文章目录
- 1. ConcurrentHashMap
- 2. LinkedBlockingQueue 阻塞队列
- 3. ConcurrentLinkedQueue
- 4. CopyOnWriteArrayList
JDK1.7 hashmap采用数组加链表头插的方式,在扩容时会出现循环死链问题,A->B->C扩容后C->B->A AB BA出现循环死链。
1. ConcurrentHashMap
HashTable
是线程安全的,但是HashTable只是单纯的方法层面上加上synchronized
。虽然安全,锁粒度太大了,所以性能不好。
口述:ConcurrentHashMap
并非锁住整个方法,像HashTable它直接在方法层面上加锁,粒度太大,ConcurrentHashMap它只锁了节点锁了一个链,而不是对整个table锁住,比如说put方法,它只是在要插入时才会加锁,吧插入的这个链表锁住,那table初始化啊、以及如果说当前位置没有元素时它会以CAS方式将节点插入,这些都没有锁,只有在插入时而且这个位置还有节点时才会加锁,粒度更小,并发性更高。而且get方法的话根本就没有加锁,也不是像HashTable那样吧方法上都加个synchronized,ConcurrentHashMap的get方法的话因为有volatile修饰table,也就保证了每次获取到的值都是最新的,也就不需要加锁。
2. LinkedBlockingQueue 阻塞队列
两把锁分别锁队头和队尾,其实就是保证消费者和生产者可以同步拿到对应的锁,你放你的我拿我的。多个消费者的话只有一个能拿到锁进行消费。
3. ConcurrentLinkedQueue
ConcurrentLinkedQueue 的设计与 LinkedBlockingQueue 非常像:
- 两把【锁】,同一时刻,可以允许两个线程同时(一个生产者与一个消费者)执行
- dummy 节点的引入让两把【锁】将来锁住的是不同对象,避免竞争
- 只是这【锁】使用了cas 来实现
例如之前讲的 Tomcat 的 Connector 结构时,Acceptor
作为生产者向 Poller 消费者传递事件信息时,正是采用了ConcurrentLinkedQueue 将 SocketChannel 给Poller
使用
4. CopyOnWriteArrayList
写入时拷贝
的思想,增删改
操作会将底层数组拷贝
一份,更改操作在新数组
上执行,这时不影响其它线程的并发读
,读-写并发,但可能读到的旧数据,有问题。
回顾:读写锁只是读读并发