一、同步类容器
同步类容器都是线程安全的,但在某些场景下可能需要加锁来保护复合操作。复合操作如:迭代(反复访问元素,遍历完容器中所有的元素)、跳转(根据指定的顺序找到当前元素的下一个元素)、以及条件运算。这些复合操作在多线程并发地修改容器时,可能会表现出意外的行为,最经典的便是ConcurrentModificationException
,原因是当容器迭代的过程中,被并发的修改了内容这是由于早期迭代器设计的时候未考虑并发修改的问题。
同步类容器:如古老的Vector
、HashTable
,这些容器的同步功能其实都是有JDK的Collections.synchronized***
等工厂方法去创建实现的。其底层的机会无非就是用传统的synchronized
关键字对每个公用的方法都进行同步,使得每次只能有一个线程访问容器的状态。这很明显不能满足我们今天互联网时代高并发的需求,保证线程安全的同时,必须要有足够好的性能。对于单独的并发修改,是线程安全的。
1. Vector & ArrayList
(1) Vector
的方法都是同步的(Synchronized
),是线程安全的(thread-safe
),而ArrayList
的方法不是,由于线程的同步必然要影响性能,因此,ArrayList
的性能比Vector
好。
(2) 当Vector
或ArrayList
中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList
只增加50%的大小,这样ArrayList
就有利于节约内存空间。
2.Hashtable & HashMap
Hashtable
和HashMap
它们的性能方面的比较类似 Vector
和ArrayList
,比如Hashtable
的方法是同步的,而HashMap
的不是。
3. ArrayList & LinkedList
ArrayList
的内部实现是基于内部数组Object[]
,所以从概念上讲,它更象数组,但LinkedList
的内部实现是基于一组连接的记录,所以,它更象一个链表结构,所以,它们在性能上有很大的差别:
(1)从上面的分析可知,在ArrayList
的前面或中间插入数据时,你必须将其后的所有数据相应的后移,这样必然要花费较多时间,所以,当你的操作是在一列 数据的后面添加数据而不是在前面或中间,并且需要随机地访问其中的元素时,使用ArrayList
会提供比较好的性能;
(2)而访问链表中的某个元素时,就必须从链表的一端开始沿着连接方向一个一个元素地去查找,直到找到所需的元素为止,所以,当你的操作是在一列数据的前面或中 间添加或删除数据,并且按照顺序访问其中的元素时,就应该使用LinkedList
了。
4. 配置集合类的初始大小
在Java集合框架中的大部分类的大小是可以随着元素个数的增加而相应的增加的,我们似乎不用关心它的初始大小,但如果我们考虑类的性能问题时,就一定要考虑尽可能地设置好集合对象的初始大小,这将大大提高代码的性能。 比如,Hashtable
缺省的初始大小为101,载入因子为0.75,即如果其中的元素个数超过75个,它就必须增加大小并重新组织元素,所以,如果你 知道在创建一个新的Hashtable
对象时就知道元素的确切数目如为110,那么,就应将其初始大小设为110/0.75=148,这样,就可以避免重 新组织内存并增加大小。Mapmap = Collections.synchronizedMap(new HashMap());
可实现线程安全
二、并发类容器:
jdk1.5以后提供了多种并发类容器来替代同步类容器从而改善性能。同步类容器的状态都是串行化的。他们虽然实现了线程安全,但是严重降低了并发性,在多线程环境时,严重降低了应用程序的吞吐量。
并发类容器是专门针对并发设计的,使用ConcurrentHashMap
来代替给予散列的传统的HashTable
,而且在ConcurrentHashMap
中,添加了一些常见复合操作的支持。
使用了CopyOnWriteArrayList
代替Vector
,并发的CopyOnWriteArraySet
,以及并发的Queue
,ConcurrentLinkedQueue
和LinkedBlockingQueue
,前者是高性能队列,后者是阻塞形式的队列,具体还有ArrayBlockingQueue
、PriorityBlockingQueue
、SynchronousQueue
等。