经常使用的一些容器例如 ArrayList、HashMap、都不是线程安全的,最简单的将这些容器变为线程安全的方式,是给这些容器全部的方法都加上 synchronized 关键字。java
Java 的 Collections 中实现了这些同步容器:数组
简单的使用以下:安全
List<String> list = Collections.synchronizedList(new ArrayList<>()); Map<Integer, String> map = Collections.synchronizedMap(new HashMap<>()); Set<String> set = Collections.synchronizedSet(new HashSet<>());
同步容器虽然简单,可是相应的效率较低,由于锁的粒度较大。数据结构
循环遍历同步容器并发
若是在遍历同步容器的时候,组合了多个方法,这会可能会存在竞态条件,仍然不是线程安全的。解决的办法即是对容器加锁。例以下面这样:性能
public static void main(String[] args) { List<String> list = Collections.synchronizedList(new ArrayList<>()); //省略添加数据的操做 String[] str = new String[list.size()]; int k = 0; synchronized (list){ Iterator<String> iterator = list.iterator(); while (iterator.hasNext()){ str[k ++] = iterator.next(); } } }
Java 中还提供了一系列并发容器,相比于同步容器,其性能更好。并发容器共分为了四类:List、Map、Set、Queue。spa
List 中一个最主要的实现类是 CopyOnWriteArrayList
,CopyOnWrite,即写时复制,这样的好处是读操做是无锁的。线程
其实现原理是内部维护了一个数组,内部变量 array 指向了这个数组。须要写时,并非在原数组上操做,而是将数组复制一份,在拷贝的数组中进行写。完成后,将 array 指向新的数组。这样一来,读写之间不互斥,效率获得了很大的提高。3d
须要注意的是 CopyOnWriteArrayList 适用于读多写少的场景,而且须要接受读写的暂时不一致,由于在写的时候,并行的读操做可能并不能立刻看到写的结果。code
Map 的两个主要实现类是 ConcurrentHashMap
和 ConcurrentSkipListMap
,二者主要的区别是:前者是无序的,后者是有序的。
在 Java 1.7 中,ConcurrentHashMap 的实现使用的是分段锁技术,其内部主要的数据结构是 Segment 和 HashEntry,ConcurrentHashMap 包含了一个 Segment 数组,每一个 Segment 又包含一个 HashEntry 数组,每一个 HashEntry 是一个存储数据的链表结构。
其中 Segment 继承了 ReentrantLock,每一个 Segment 都有对应的锁,须要修改数据的时候,须要获取这把锁。修改不一样的 Segment 数据,则彻底能够并行,效率获得了提高。示意图以下:
Java 1.8 又对 ConcurrentHashMap 作了较大的改进,放弃了分段锁的技术。结构和 Java 1.8 中的 HashMap 相似,采用的是数组+链表/红黑树来实现。为了下降哈希冲突的成本,在链表长度超过 8 时,将链表转换为红黑树。使用 CAS 和 synchronized 解决并发问题,锁住链表或者红黑树的头节点,只要没有哈希冲突,则不会出现并发问题。示意图以下:
ConcurrentSkipListMap 保证有序的主要缘由是,底层使用的是跳表这种数据结构,关于跳表的介绍,你能够查看数据结构中的内容。
Set 的两个实现是 CopyOnWriteArraySet
和 ConcurrentSkipListSet
。
和前面说到的 CopyOnWriteArrayList 、ConcurrentSkipListMap 实现的原理相似。
队列能够从两方面进行分类:
Java 中,单端队列使用 queue 标识,双端队列使用 deque 标识。
经常使用的实现类有:ArrayBlockingQueue
、LinkedBlockingQueue
、PriorityQueue
。
其实现类是 ConcurrentLinkedQueue
其实现类是 LinkedBlockingDeque
其实现类是 ConcurrentLinkedDeque
使用其实都很是地简单,就是入队出队之类的操做,这里再也不赘述了。