1.arraylist在默认容量是10,每次进行add的时候会检查容量,如果需要扩容则增加1.5倍,如果仍然小,则设为增加后的长度大小
2.copyonwritearraylist 保证线程安全的方法是:不对读进行限制,写的操作加重入锁,写的时候拷贝一份数组,并将新数据写入,完成后再将引用返回。
重新设置引用是一个原子操作。适用于写的操作比较少,重点为读操作的并发情况。 在迭代的时候,由于写操作不会更改当前迭代器的容器引用,因此不 会有concurrent异常。
3. collections.synchronizedlist : 对每个方法(迭代器除外)加了对象锁。所以在进行单次调用list方法的时候保证线程安全。但是可能会存在竞态条件。所以客户端进行
使用的时候要注意加外部锁。一个典型的例子是,自动以一个方法,需要先调用list的读方法,再改写list,虽然两个方法都是线程安全的,但是在两个方法调用 期间可能会有其他线程对list进行修改,这会导致迭代器最终抛出异常。此时需要在客户端方法上添加synchronize(list)进行外部加锁,这样可以保证两个操作 之间不会有其他线程修改list(方法的对象锁不能使自定义的类,这样没有办法阻止其他线程访问list) 此竞态条件也会出现在其他并发容器中,包括上面那一条
相比于copyonwritearraylist,写的效率明显更高,读的效率略微下降
容器3的迭代器是不安全的!
4.HashMap http://blog.csdn.net/jzhf2012/article/details/8540670
默认大小是16,且容量必须是2的次幂,有最大值为2的30次幂。默认因子为0.75,为扩容的临界系数。
底层数据结构为一个数组,每个元素存一个链表,链表元素为Entry<K,V>。 定位元素的过程是: 1.通过key的hashcode值来计算出一个新的hash值(防止原来的 hashcode写的不好),通过新的hash和底层数组table的长度来定位index
2.通过index找到数组中的第一个Entry元素. 循环链表查找是否有key与当前元素相同。 判断相同的方法为:先判断hash值是否相同,在判断equals和==。
所以自定义类的时候,如果要用到map,切记重写hashcode和equals方法
HashMap为什么是线程不安全的:当添加或删除的时候,如果操作的key的hash值一样,会导致多个线程对同一个链表进行操作(结构改变),这会导致后一个线 程的操作覆盖前一个线程(1.同时在链表上添加entry 2.同时删除enry 3.重新分配哈希表,数组结构更新)
5.LinkedHashMap :可以预知排序顺序,两种:插入时的顺序,或者是访问的顺序
Entry的数据结构比Hashmap中的Entry多了两个变量,before和after,以此来建立Entry的链表
实际上就是一个Hashmap+Linklist
在底层数据结构中增加了一个双向链表来储存Entry引用,设立一个boolean的标志位,来决定是已插入顺序储存还是最近访问顺序储存
若以访问顺序储存,则每次操作一个节点后,调整链表顺序。
6.TreeMap: http://blog.csdn.net/chenssy/article/details/26668941
7.concurrentskiplistmap:http://blog.csdn.net/sunxianghuang/article/details/52221913
对高并发有较好的支持,底层实现是利用跳表的数据结构。Entry是排序的。查找等操作的时间复杂度是log(n)。
利用CAS方法保证线程安全
容器的三种安全并发方式比较
1.CopyOnWrite
写操作加锁,并在一个副本上进行写操作,同时读操作不加锁,读的时候读的是原始容器。写操作执行的最后一步是给引用赋值最新的副本容器,这是一个原始操作。
由于集成体系不同,他没有modelcount变量,因此不会抛出快速失败异常
性能上,写操作性能差,读操作性能高
2.Collections.synchronized
对非安全容器内部方法加锁,迭代器不加锁。所以,在用容器内部迭代器时,另一个线程的写操作会导致迭代快速失败。
写性能高于copyonwrite,读性能略低于前者
注:1和2继承的体系完全不同,前者继承了abstractlist骨架类,后者实现了randomaccess接口
3.Concurrent(HashMap) http://www.cnblogs.com/ITtangtang/p/3948786.html
采用了分段锁,将一个table分成了若干segment,每个segment有自己的锁,所以可以并发访问不同的segment。
数据结构(以map为例):一个HashEntry节点中的key,next和hash都是final的,value是 volatile的,所以读操作不需要加锁
由于next是final的,所以remove必须先clone删除点之前所有的节点,然后将最后一个节点的next指向删除点后一个节点
插入必须在头部插入
并发效率高,但若一致性。不会快速失败,迭代实际上是迭代底层的数组,已经迭代过的值改变不影响,未迭代的值变化则会体现在接下来的读取中
高并发的时候尽量使用2和3,具体选择根据读写情况决定