- 同步容器和并发容器
非线程安全的容器:ArrayList、LinkedList、HashMap等等
同步容器:Vector、Stack、HashtableCollections.synchronizedXxx()等等
并发容器:ConcurrentHashMap、CopyOnWriteArrayList、CopyOnWriteArraySet等等
同步容器和并发容器的区别:
同步容器使用synchronized进行同步,它的操作是按顺序执行的,并发性很差;并发容器使用不同的锁机制实现了高并发的访问,可以并发的对容器进行遍历及写入;并且它不会抛出ConcurrentModificationException异常。
ConcurrentHashMap:代替同步的HashMap。它并不是简单的使用互斥锁,它的锁的粒度更细(分段锁),可以并发的进行读写操作而不是向Hashtable那样加锁只能一个线程访问。
ConcurrentHashMap的size()和isEmpty()返回的是近似值(但是一般是准确的),因为这俩货都需要对Map进行整个的遍历,在遍历过程中不能保证size和isEmpty的正确性。但是在并发环境下,因为容器内容会不断变化,所以就牺牲它俩来提供更好的并发性能。
CopyOnWriteArrayList:即“写入时复制”,用来代替同步的List。大概意思就是在对List进行写操作时,在内部复制一份对象,然后对这个副本进行操作。
- ConcurrentHashMap都不会抛出ConcurrentModficationException
ConcurrentHashMap举例:
新建两个线程,一个对ConcurrentHashMap进行写入操作,另一个进行不断遍历操作。
public class ConcurrentHashMapTest {
private static ConcurrentHashMap<String,Integer> map = new ConcurrentHashMap<>();
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
int i=0;
while (true){
map.put("key"+i,i++);
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true){
for(String key : map.keySet()){
System.out.println("key="+key+",value="+map.get(key));
}
System.out.println("-----");
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
输出:
然而如果将ConcurrentHashMap换成HashMap或者是同步的Hashtabled都会抛出ConcurrentModificationException异常。因为HashMap不是线程安全的,而虽然Hashtable是线程安全定,但是它并不支持在多线程下进行并发的访问,仍然需要手都同步。