并发容器ConcurrentLinkedQueue原理解析:
无界非阻塞队列,它是一个基于链表的无界线程安全队列。该队列的元素
遵循先进先出的原则。头是最先加入的,尾是最近加入的。插入元素是追加到
尾上。提取一个元素是从头提取。
大家可以看成是
LinkedList
的并发版本,常用方法:
concurrentLinkedQueue.add("c");
concurrentLinkedQueue.offer("d"); //
将指定元素插入到此队列的尾部。
concurrentLinkedQueue.peek(); //
检索并不移除此队列的头,如果此队列为 空,则返回 null
。
concurrentLinkedQueue.poll(); //
检索并移除此队列的头,如果此队列为空, 则返回 null
。
写时复制容器
什么是写时复制容器?
CopyOnWriteArrayList
和
CopyOnWriteArraySet
CopyOnWrite
容器即写时复制的容器。通俗的理解是当我们往一个容器添加 元素的时候,不直接往当前容器添加,而是先将当前容器进行 Copy
,复制出一 个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指 向新的容器。 这样做的好处是我们可以对 CopyOnWrite
容器进行并发的读,而不需要加锁, 因为当前容器不会添加任何元素。所以 CopyOnWrite
容器也是一种读写分离的思 想,读和写不同的容器。如果读的时候有多个线程正在向 CopyOnWriteArrayList 添加数据,读还是会读到旧的数据,因为写的时候不会锁住旧的 CopyOnWriteArrayList。
CopyOnWrite
并发容器用于对于绝大部分访问都是读,且
只是偶尔写
的并发 场景。比如白名单,黑名单,商品类目的访问和更新场景,假如我们有一个搜索 网站,用户在这个网站的搜索框中,输入关键字搜索内容,但是某些关键字不允 许被搜索。这些不能被搜索的关键字会被放在一个黑名单当中,黑名单每天晚上 更新一次。当用户搜索时,会检查当前关键字在不在黑名单当中,如果在,则提 示不能搜索。
使用 CopyOnWriteMap
需要注意两件事情:
1.
减少扩容开销。根据实际需要,初始化
CopyOnWriteMap
的大小, 避免写时 CopyOnWriteMap
扩容的开销。
2.
使用批量添加。因为每次添加,容器每次都会进行复制,所以减少添加 次数,可以减少容器的复制次数。
写时复制容器的问题
性能问题
每次修改都创建一个新数组,然后复制所有内容,如果数组比较大,修改操 作又比较频繁,可以想象,性能是很低的,而且内存开销会很大。
数据一致性问题。
CopyOnWrite
容器只能保证数据的最终一致性,不能保证数据的实时一致性。 所以如果你希望写入的的数据,马上能读到,不要使用 CopyOnWrite
容器。