集合类Map ConcurrentHashMap Collections.synchronizedMap()
多线程安全:
三个独立的功能——原子性、可见性和顺序性。
原子性非常简单——同步实施一个可重入的(reentrant)互斥,防止多于一个的线程同时执行由一个给定的监视器保护的代码块
JVM在获得和释放监视器的时候执行内存壁垒(memorybarrier):
一个线程在获得一个监视器之后,它执行一个读屏障(readbarrier):
——使得缓存在线程局部内存(比如说处理器缓存或者处理器寄存器)中的所有变量都失效,处理器重新从主存中读取同步代码块使用的变量。
一个线程在释放监视器时,线程会执行一个写屏障(writebarrier):
——将所有修改过的变量写回主存。
互斥独占和内存壁垒结合使用意味着只要您在程序设计的时候遵循正确的同步法则(写一个后面可能被其他线程访问的变量,或者读取一个可能最后被另一个线程修改的变量时,都要使用同步),每个线程都会得到它所使用的共享变量的正确的值
java.util包中Map相关的集合类的类图
接口Map是整个类图的跟,Map往下又提供了两个接口:ConcurrentMap和SortedMap。
ConcurrentMap线程安全的Map接口
SortedMap则是支持排序的Map接口
具体的实现类中,常用的就属Hashtable、HashMap和TreeMap
Map用于存储“key-value”元素对,它将一个key映射到一个而且只能是唯一的一个value。
Map可以使用多种实现方式:
HashMap的实现采用的是hash表;
TreeMap采用的是红黑树
Hashtable 和 HashMap
1、Hashtable和HashMap都实现了Map接口,但Hashtable的实现是基于Dictionary抽象类。
2、HashMap中,key和value可以为null
Hashtable中,无论是key还是value都不能为null
(HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。应该用containsKey()方法来判断是否存在某个键)。
3、Hashtable是线程安全的,采用的锁机制是一次锁住整个hash表,同一时刻只能由一个线程对其进行操作;
HashMap则不是线程安全的。在多线程环境中,需要手动实现同步机制。
HashMap则不是线程安全的
Collections类中提供了一个方法返回一个同步版本的HashMap用于多线程的环境:
SynchronizedMap类是定义在Collections中的一个静态内部类。它实现了Map接口,并对其中的每一个方法实现,通过synchronized关键字进行了同步控制。
Collections.synchronizedMap()当并发数很多的时候,出现其他请求等待情况,因为synchronizedMap会锁住所有的资源。
public static Map synchronizedMap(Map m) { //返回的是一个SynchronizedMap的实例
return new SynchronizedMap(m);
}
Map集合共提供了三种方式来分别返回键、值、键值对的集合:
Set<K> keySet();
Collection values();
Set<Map.Entry<K,V>> entrySet(); // 一个Entry对应一个key-value
使用示例:
//Iterator keys = map.keySet().iterator();
//synchronized (map) {
// while(keys.hasNext()){
// map.get(keys.next());
// }
//}
Map<String,String> map = Collections.synchronizedMap(new TreeMap<String,String>());
map.put("key1","value1");
map.put("key2","value2");
Set<Entry<String,String>> entries = map.entrySet();
Iterator<Entry<String,String>> iter = entries.iterator();
synchronized (map) {
while(iter.hasNext()){
System.out.println(iter.next());
//迭代元素时可能会抛出ConcurrentModificationException异常,所以可加上同步机制synchronized
map.remove("key2");
}
}
有两种语言机制可用于保证跨线程内存操作的一致性:
synchronized和volatile:
synchronized 是Java语言关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码,其内部实现锁机制。synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块
volatile变量放在主存区上,使用该变量的每个线程,都将从主存区拷贝一份到自己的工作区上进行操作,Java内存模型负责各个线程的工作区与主存区的该字段的值保持同步,即一致性.
volatile是处理多线程锁的替代方案,对应有时需要实时的修改共享资源的变量,被volatile修复的变量的值可以立刻被业务方取得最新的值。
volatile同步机制不同于synchronized, volatile是内存同步,synchronized不仅包含内存同步(一致性),且保证线程互斥(互斥性)
ConcurrentMap接口和它的一个实现类ConcurrentHash:
ConcurrentHashMap中采用分段锁机制(与Hashtable以及SynchronizedMap中不同的锁机制,
摒弃了单一的map范围的锁,取而代之的是由16个锁组成的集合,其中每个锁负责保护hashbucket的一个子集):
ConcurrentHashMap默认将hash表分为16个段,诸如get,put,remove等常用操作只锁当前需要用到的段。原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的(读操作大部分时候都不需要用到锁。只有在size等操作时才需要锁住整个hash表)
迭代方面:
ConcurrentHashMap使用了一种不同的迭代方式。
当iterator被创建后集合再发生改变就不再是抛出ConcurrentModificationException,
而是在改变时,new新的数据(从而不影响原有的数据),iterator完成后再将头指针替换为新的数据 ,这样iterator线程可以使用原来老的数据,而写线程也可以并发的完成改变
Map<String, String> map3 = new ConcurrentHashMap<String, String>();
map.put("key1","value1");
map.put("key2","value2");
Set<Entry<String,String>> entries = map.entrySet();
Iterator<Entry<String,String>> iter = entries.iterator();
while(iter.hasNext()){
System.out.println(iter.next());
map.remove("key2");
}