http://chenzehe.iteye.com/blog/1779990
Java在JDK1.5之前基本上对所有集合都实现了线程同步版本synchronized*,用集合工具类Collections即可得到,如下都为Collections中的方法:
static <T> Collection<T>
synchronizedCollection(Collection<T> c)
返回指定 collection 支持的同步(线程安全的)collection。
static <T> List<T>
synchronizedList(List<T> list)
返回指定列表支持的同步(线程安全的)列表。
static <K,V> Map<K,V>
synchronizedMap(Map<K,V> m)
返回由指定映射支持的同步(线程安全的)映射。
static <T> Set<T>
synchronizedSet(Set<T> s)
返回指定 set 支持的同步(线程安全的)set。
static <K,V> SortedMap<K,V>
synchronizedSortedMap(SortedMap<K,V> m)
返回指定有序映射支持的同步(线程安全的)有序映射。
static <T> SortedSet<T>
synchronizedSortedSet(SortedSet<T> s)
返回指定有序 set 支持的同步(线程安全的)有序 set。
其内部实现是在内部维护一个对应的集合类型,然后相应所有的操作都加上同步关键字synchronized,同步方法内再调用内部集合的方法,如下为synchronizedMap的实现:
http://xyiyy.iteye.com/blog/361905
不过在使用Iterator遍历对象时,仍必须实现同步化。因为这样的List使用iterator()方法返回的Iterator对象,并没有保证线程安全。一个实现遍历的例子如下:
List list = Collections.synchronizedList(new ArrayList());
...
synchronized(list)
{
Iterator i = list.iterator();
while(i.hasNext())
{
foo(i.next());
}
}
J2SE5.0之后,新增了java.util.concurrent这个包,其中包括一些确保线程安全的Collection类,如ConcurrentHashMap、CopyOnWriteArrayList、CopyOnWriteArraySet等。这些对象与先前介绍的Map、List、Set等对象是相同的,不同的是增加了同步的功能,而且依对象存取时的需求不同而有不同的同步化实现,以同时确保效率和安全性。例如ConcurrentHashMap对Hash Table中不同的区段进行同步化。
使用Collections.synchronizedList/Set/Map可能获得一个线程安全的容器,但是效率并不是最高
对于Map而言,推荐使用ConcurrentHashMap
ConcurrentlinkedQueue的效率也高于BlockingQueue,BlockingQueue是一个接口,对应有两个具体的类,ArrayBlockingQueue和LinkedBlockingQueue
LinkedBlockingDeque没有进行读写锁的分离,同一时间只能有一个线程访问,因此,效率低于LinkedBlockingQueue
这是因为Concurrent系列都通过冗余空间获得了近乎无锁的效率
1. CopyOnWriteArrayList
适用于大多是读,而写很少的环境下。
在那些遍历操作大大地多于插入或移除操作的并发应用程序中,一般用 CopyOnWriteArrayList
类替代 ArrayList
。如果是用于存放一个侦听器(listener)列表,例如在AWT或Swing应用程序中,或者在常见的JavaBean中,那么这种情况很常见(相关的CopyOnWriteArraySet
使用一个 CopyOnWriteArrayList
来实现 Set
接口) 。
如果您正在使用一个普通的 ArrayList
来存放一个侦听器列表,那么只要该列表是可变的,而且可能要被多个线程访问,您 就必须要么在对其进行迭代操作期间,要么在迭代前进行的克隆操作期间,锁定整个列表,这两种做法的开销都很大。当对列表执行会引起列表发生变化的操作时, CopyOnWriteArrayList
并不是为列表创建一个全新的副本,它的迭代器肯定能够返回在迭代器被创建时列表的状态,而不会抛出 ConcurrentModificationException
。在对列表进行迭代之前不必克隆列表或者在迭代期间锁 定列表,因为迭代器所看到的列表的副本是不变的。换句话说, CopyOnWriteArrayList
含有对一个不可变数组的一个可变的引用,因此,只要保留好那个引用,您就可以获得不可变的线程安全性的好处,而且不用锁 定列表。
package javatest;
import java.io.*;
import java.io.FileInputStream;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.*;
import info.*;
public class test {
class writer implements Runnable {
List<Integer> ls;
public writer(List<Integer> ls) {
this.ls = ls;
}
public void run(){
for (int i = 6; i < 12; ++i) {
ls.add(i);
System.out.println("add " + i);
try {
Thread.sleep(10);
}
catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
class del implements Runnable {
List<Integer> ls;
public del(List<Integer> ls) {
this.ls = ls;
}
public void run() {
int i = 0;
while (i < 6 && !ls.isEmpty()) {//unsafe
++i;
int len = ls.size();
if (len >= 2) {
System.out.println("rm " + ls.get(len - 2));
ls.remove(len - 2);//unsafe
}
try {
Thread.sleep(1000);
}
catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
test t = new test();
CopyOnWriteArrayList<Integer> ls = new CopyOnWriteArrayList<Integer>(Arrays.asList(1,2,3,4));
new Thread(t.new writer(ls)).start();
new Thread(t.new del(ls)).start();
Iterator<Integer> it = ls.iterator();
while (it.hasNext()) {
System.out.println(it.next());
//it.remove();
try {
Thread.sleep(1000);
}
catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
创建迭代器时, 迭代器就初始化了当前数组的快照. 就算迭代期间进行了写操作, 也不会影响到迭代器中的snapshot数组. 所以CopyOnWriteArrayList返回的迭代器只反应迭代发生时CopyOnWriteArrayList对象所持有的集合, 迭代期间发生的改变不会反应出来.所以在用iterator遍历的时候不需要额外的同步操作,但是it.remove不是同步操作,如果第66行不被注释,会产生异常
http://ifeve.com/why-is-there-not-concurrent-arraylist-in-java-util-concurrent-package
问:JDK 5在java.util.concurrent里引入了ConcurrentHashMap,在需要支持高并发的场景,我们可以使用它代替HashMap。但是为什么没有ArrayList的并发实现呢?难道在多线程场景下我们只有Vector这一种线程安全的数组实现可以选择么?为什么在java.util.concurrent 没有一个类可以代替Vector呢?
答:我认为在java.util.concurrent包中没有加入并发的ArrayList实现的主要原因是:很难去开发一个通用并且没有并发瓶颈的线程安全的List。
像ConcurrentHashMap这样的类的真正价值(The real point / value of classes)并不是它们保证了线程安全。而在于它们在保证线程安全的同时不存在并发瓶颈。举个例子,ConcurrentHashMap采用了锁分段技术和弱一致性的Map迭代器去规避并发瓶颈。
所以问题在于,像“Array List”这样的数据结构,你不知道如何去规避并发的瓶颈。拿contains() 这样一个操作来说,当你进行搜索的时候如何避免锁住整个list?
另一方面,Queue 和Deque (基于Linked List)有并发的实现是因为他们的接口相比List的接口有更多的限制,这些限制使得实现并发成为可能。