【关于作者】
关于作者,目前在蚂蚁金服搬砖任职,在支付宝营销投放领域工作了多年,目前在专注于内存数据库相关的应用学习,如果你有任何技术交流或大厂内推及面试咨询,都可以从我的个人博客(https://0522-isniceday.top/)联系我
1.同步容器
同步容器,指的是通过synchronized关键字来保证原子性,类似如下的包装类都是基于此考量:
ArrayList、HashSet 和 HashMap 包装成了线程安全的 List、Set 和 Map。
List list = Collections.
synchronizedList(new ArrayList());
Set set = Collections.
synchronizedSet(new HashSet());
Map map = Collections.
synchronizedMap(new HashMap());
但是针对包装后的容器采用迭代器遍历,还是会存在原子性的问题,因此这种场景也可以进行synchronized进行包装
List list = Collections.
synchronizedList(new ArrayList());
synchronized (list) {
Iterator i = list.iterator();
while (i.hasNext())
foo(i.next());
}
Java 提供的同步容器还有 Vector、Stack 和 Hashtable,这三个容器不是基于包装类实现的,但同样是基于 synchronized 实现的,对这三个容器的遍历,同样要加锁保证互斥
2.并发容器
Java1.5之前都是通过同步容器实现线程安全,但是使用synchronized串行度太大了,性能太差。JDK1.5之后提供了性能更高的容器,称之为并发容器
1.1.List
List只有一个实现类:CopyOnWriteArrayList
其采取的机制其实就是写时复制(CopyOnWrite)机制,读操作的时候不进行处理,写操作的时候复制数组并在复制的数组执行写操作,最后将数据的引用指向新的数组
![image-20210816172935851](https://i-blog.csdnimg.cn/blog_migrate/a1d1a9113e019929f032d528b2733aaf.png)
![image-20210816172944273](https://i-blog.csdnimg.cn/blog_migrate/66886288e704b771e3ec0aa702ab58ef.png)
需要注意的几个地方:
- 数据会存在短暂的不一致,写入的新元素无法立即被读取到
- CopyOnWriteArrayList 迭代器是只读的,不支持增删改。因为迭代器遍历的仅仅是一个快照,而对快照进行增删改是没有意义的
1.2.Map
Map接口的两个实现主要是:ConcurrentHashMap、ConcurrentSkipListMap。它们从应用的角度来看,主要区别在于ConcurrentHashMap 的 key 是无序的,而 ConcurrentSkipListMap 的 key 是有序的
![image-20210816174536939](https://i-blog.csdnimg.cn/blog_migrate/15d26e5c57ead211bf5afb667914ad7a.png)
1.3.Set
Set 接口的两个实现是 CopyOnWriteArraySet 和 ConcurrentSkipListSet,使用场景可以参考前面讲述的 CopyOnWriteArrayList 和 ConcurrentSkipListMap
1.4.Queue
可以按照两个维度针对队列进行分类:
- 阻塞或非阻塞,所谓阻塞是指队满时,入队操作阻塞。队空时,出队操作阻塞。阻塞队列都用 Blocking 关键字标识
- 单端或双端队列,单端是只允许一段出队或入队,双端是允许双端进行入队出队操作,单端队列使用 Queue 标识,双端队列使用 Deque 标识
此时可将Queue分为如下四大类:
- 单端阻塞队列:其包括ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue、LinkedTransferQueue、PriorityBlockingQueue 和 DelayQueue。内部一般会持有一个队列结构,其实现包括数组,例如ArrayBlockQueue,或链表,例如LinkedBlockQueue。甚至还可以不持有队列,例如SynchronousQueue,此时生产者的入队操作必须等待消费者的出队操作。而 LinkedTransferQueue 融合 LinkedBlockingQueue 和 SynchronousQueue 的功能,性能比 LinkedBlockingQueue 更好。PriorityBlockQueue支持按照优先级出队(其实现是一个二叉堆),DelayQueue支持延时出队。
- 双端阻塞队列:其包括LinkedBlockDeque
- 单端非阻塞队列:其包括ConcurrentLinkedQueue
- 双端非阻塞队列:其实现是 ConcurrentLinkedDeque
只有 ArrayBlockingQueue 和 LinkedBlockingQueue 是支持有界的,所以在使用其他无界队列时,一定要充分考虑是否存在导致 OOM 的隐患