数据强、弱一致性:
严格一致性(强一致性):所有的读写操作都按照全局时钟下的顺序执行,且任何时刻线程读取到的缓存数据都是一样的,Hashtable 就是严格一致性;
顺序一致性:多个线程的整体执行可能是无序的,但对于单个线程而言执行是有序的,要保证任何一次读都能读到最近一次写入的数据,volatile 可以阻止指令重排序,所以修饰的变量的程序属于顺序一致性;
弱一致性:不能保证任何一次读都能读到最近一次写入的数据,但能保证最终可以读到写入的数据,单个写锁 + 无锁读,就是弱一致性的一种实现。
MAP容器
HashMap
非线程安全
原因:JDK1.7之前,并发场景可能出现死循环(看过几篇文章说是在扩容时出现循环列表,JDK1.8之后采用红黑树说会出现数据丢失以及不准确(没遇到,左旋or右旋+查询+查询导致?))
HashTable
线程安全
强一致性场景
数据量小时使用
缺:使用Synchronized同步锁修饰put get remove方法,高并发下有大量锁竞争
ConcurrentHashMap
线程安全
数据量不是特别大时使用
相比HashTable优点:JDK1.7之前分段锁,JDK1.8之后先使用Synchronized(但是先使用CAS再升级锁 JDK1.8优化了Synchronized)
适应场景:统计销量TOP10
缺:get size 等方法没有使用锁,弱一致性。
ConcurrentSkipListMap
基于TreeMap(单线程使用)的设计原理实现,不过TreeMap使用红黑树,其使用跳表
优点:存取平均时间复杂度O(log(n)),适用于大数据量存取的场景,最常见的是基于跳跃表实现的数据量比较大的缓存。
适用场景:提醒用户手机卡实时流量不足,先同步用户实时流量,再通过手机端订单触发查询功能,如果流量不足,就弹出系统通知。数据量千万级,且存在大量增删改操作。
特点,用户量大,并发量高,写入多于查询。
List容器
Vector
线程安全
强一致性
基于Synchronized同步锁实现线程安全
缺点:高并发下,读远大学写,大量锁竞争
CopyOnWriteArrayList
线程安全(写操作修改副本使用Synchronized)
读操作无锁,写操作使用新副本,读写分离。
适用场景:大量读,少量改。例:黑白名单
Queue容器
ConcurrentLinkedQueue
线程安全
无锁 使用CAS
使用场景:
1.写多读少,该队列基于链表实现,新增和删除元素性能较高(应该是尾部和头部)
2.写操作使用cas操作,性能较高。
3.单生产者,多消费者
缺点:
链表无界,容易造成大量内存使用,甚至内存溢出,需要控制容量。
LinkedTransferQueue
一个由链表结构组成的无界阻塞TransferQueue队列。相对于其他阻塞队列,LinkedTransferQueue多了tryTransfer和transfer方法。
ArrayBlockingQueue(一把全局锁)
一个基于数组结构实现的有界阻塞队列,按 FIFO(先进先出)原则对元素进行排序,使用 ReentrantLock、Condition 来实现线程安全;
LinkedBlockingQueue
存取两把锁
一个基于链表结构实现的阻塞队列,同样按 FIFO (先进先出) 原则对元素进行排序,使用 ReentrantLock、Condition 来实现线程安全,吞吐量通常要高于 ArrayBlockingQueue;
disruptor
PriorityBlockingQueue:
一个具有优先级的无限阻塞队列,基于二叉堆结构实现的无界限(最大值 Integer.MAX_VALUE - 8)阻塞队列,队列没有实现排序,但每当有数据变更时,都会将最小或最大的数据放在堆最上面的节点上,该队列也是使用了 ReentrantLock、Condition 实现的线程安全;
SynchronousQueue
一个不存储多个元素的阻塞队列,每次进行放入数据时, 必须等待相应的消费者取走数据后,才可以再次放入数据,该队列使用了两种模式来管理元素,一种是使用先进先出的队列,一种是使用后进先出的栈,使用哪种模式可以通过构造函数来指定。