HashTable和Vector
遗留的线程安全实现:效率较低
HashTable:map的实现
Vector:list的实现
Collections中的带synchronized修饰实现类
如synchronizedMap。synchronizedList等
方法内部都加一个synchronized修饰,同步块中调用原来的方法。属于装饰器模式,对原来的List或者Map功能进行装饰
JUC
java.util.concurrent.*下面的集合类
主要分为三类:
Blocking类:大部分实现基于锁,并提供用来阻塞的方法
CopOnWrite:拷贝后修改,容器修改开销相对较重
Concurrent:
内部很多操作使用 cas 优化,一般可以提供较高吞吐量
弱一致性
遍历时弱一致性,例如,当利用迭代器遍历时,如果容器发生修改,迭代器仍然可以继续进行遍历,这时内容是旧的
求大小弱一致性,size 操作未必是 100% 准确
读取弱一致性
遍历时如果发生了修改,对于非安全容器来讲,使用 fail-fast 机制也就是让遍历立刻失败,抛出ConcurrentModificationException,不再继续遍历
ConcurrentHashMap
原理见java面试题整理中的ConcurrentHashMap,地址:[java面试题](https://blog.csdn.net/MpenggegeM/article/details/123022267?spm=1001.2014.3001.5502)
LinkedBlockingQueue
原理:
1、对象中维护一个内部Node对象,存放数据item内容,和next指向下一个Node
2、初始化:生成一个Dummy Node节点,用来占位,item = null,next=null,Queue的head和last都指向这个Dummy对象
3、入队:将last.next指向新加入的node,将last指向新加入的node;
4、出队:
用first变量指向head.next,也就是第一个有值的节点。
将h=head对象,h.next=h指向自己,方便安全地进行GC
将first.item赋值给变量返回。first.item又置为null,意思是变为Dummy节点。
将head指向first
总体描述就是,获得Dummy后面第一个节点的值,将这个节点置为Dummy,将原来的Dummy垃圾回收;
加锁分析:
1、生产者消费者使用不同的锁,putlock和takelock
2、当链表长度只有1个Dummy数据时,takelock的线程会阻塞
3、当同类型锁await和signal时,如果put还有空位,或者还能take,只signal一个线程进行操作,减少竞争;
性能比较:
主要列举 LinkedBlockingQueue 与 ArrayBlockingQueue 的性能比较:
Linked 支持有界,Array 强制有界
Linked 实现是链表,Array 实现是数组
Linked 是懒惰的,而 Array 需要提前初始化 Node 数组
Linked 每次入队会生成新 Node,而 Array 的 Node 是提前创建好的
Linked 两把锁,Array 一把锁
ConcurrentLinkedQueue
ConcurrentLinkedQueue 的设计与 LinkedBlockingQueue 非常像
1、两把【锁】,同一时刻,可以允许两个线程同时(一个生产者与一个消费者)执行
2、dummy 节点的引入让两把【锁】将来锁住的是不同对象,避免竞争
3、只是这【锁】使用了 cas 来实现
CopyOnWriteArrayList
1、写操作时,进行复制后再操作覆盖。因此不影响读,读线程无需加锁。
2、一般用于读多写少的状况
3、弱一致性,可能读的旧数据
|