public class HashTest {
static Map map = new HashMap<>();
public static void main(String[] args) {
for (int i = 0; i < 2; i++) {
new Thread(() -> {
String key = Thread.currentThread().getName();
String value = String.valueOf(new Random().nextInt());
map.put(key, value);
System.out.println(key + "\t" + map.get(key));
}).start();
}
}
}
HashMap普通的遍历方式在遍历时是不允许删除的
public static void main(String[] args) {
Map hashMap = new HashMap() {
{
put(0, "星期一");
put(1, "星期二");
put(2, "星期三");
put(3, "星期四");
put(4, "星期五");
put(5, "星期六");
put(6, "星期日");
}
};
hashMap.forEach((key, value) -> {
if (key.intValue() <=4) hashMap.remove(key);
});
}
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.HashMap.forEach(HashMap.java:1291)
at com.company.example.hash.HashMapTest.main(HashMapTest.java:30)
使用迭代器方式在遍历的时候可以删除
public static void main(String[] args) {
Map hashMap = new HashMap() {
{
put(0, "星期一");
put(1, "星期二");
put(2, "星期三");
put(3, "星期四");
put(4, "星期五");
put(5, "星期六");
put(6, "星期日");
}
};
Iterator
ConcurrentHashMap: 使用普通的遍历方式可以在遍历的时候删除操作
public static void main(String[] args) {
Map hashMap = new ConcurrentHashMap() {
{
put(0, "星期一");
put(1, "星期二");
put(2, "星期三");
put(3, "星期四");
put(4, "星期五");
put(5, "星期六");
put(6, "星期日");
}
};
hashMap.forEach((key, value) -> {
if (key.intValue() <=4) hashMap.remove(key);
});
System.out.println(hashMap);
}
ConcurrentHashMap是并发效率更高的Map,用来替换其他线程安全的Map容器,比如Hashtable和Collections.synchronizedMap。
public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
implements ConcurrentMap<K,V>, Serializable {
public ConcurrentHashMap();
public ConcurrentHashMap(int initialCapacity);
public ConcurrentHashMap(Map extends K, ? extends V> m);
public V get(Object key);
public V put(K key, V value);
// 如果key对应的value不存在,则put进去,返回null。否则不put,返回已存在的value
public V putIfAbsent(K key, V value)
// 如果key对应的当前值是oldValue,则替换为newValue,返回true。否则不替换,返回false
public boolean replace(K key, V oldValue, V newValue);
// 如果key对应的值是value,则移除K-V,返回true。否则不移除,返回false
public V remove(Object key);
}
ConcurrentHashMap的简要总结:
public V get(Object key)不涉及到锁,也就是说获得对象时没有使用锁;
put、remove方法要使用锁,但并不一定有锁争用,原因在于ConcurrentHashMap将缓存的变量分到多个Segment,每个Segment上有一个锁,只要多个线程访问的不是一个Segment就没有锁争用,就没有堵塞,各线程用各自的锁,ConcurrentHashMap缺省情况下生成16个Segment,也就是允许16个线程并发的更新而尽量没有锁争用;
Iterator对象的使用,不一定是和其它更新线程同步,获得的对象可能是更新前的对象,ConcurrentHashMap允许一边更新、一边遍历,也就是说在Iterator对象遍历的时候,ConcurrentHashMap也可以进行remove,put操作,且遍历的数据会随着remove,put操作产出变化,所以希望遍历到当前全部数据的话,要么以ConcurrentHashMap变量为锁进行同步(synchronized该变量),要么使用CopiedIterator包装iterator,使其拷贝当前集合的全部数据,但是这样生成的iterator不可以进行remove操作。
Hashtable和ConcurrentHashMap的不同点:
Hashtable对get,put,remove都使用了同步操作,它的同步级别是正对Hashtable来进行同步的,也就是说如果有线程正在遍历集合,其他的线程就暂时不能使用该集合了,这样无疑就很容易对性能和吞吐量造成影响,从而形成单点。而ConcurrentHashMap则不同,它只对put,remove操作使用了同步操作,get操作并不影响,详情请看以上第1,2点,当前ConcurrentHashMap这样的做法对一些线程要求很严格的程序来说,还是有所欠缺的,对应这样的程序来说,如果不考虑性能和吞吐量问题的话,个人觉得使用Hashtable还是比较合适的;
Hashtable在使用iterator遍历的时候,如果其他线程,包括本线程对Hashtable进行了put,remove等更新操作的话,就会抛出ConcurrentModificationException异常,但如果使用ConcurrentHashMap的话,就不用考虑这方面的问题了,详情请看以上第3点;
ConcurrentHashMap是线程安全的,但是ConcurrentHashMap只能保证自己是安全的,并不能保证其它业务逻辑是线程安全的
public static void main(String[] args) throws InterruptedException {
String key = "key";
Map hashMap = new ConcurrentHashMap<>();
CountDownLatch countDownLatch = new CountDownLatch(2);
Runnable runnable = () -> {
for (int i = 0; i < 5; i++) {
Integer value = hashMap.get(key);
if (value == null) {
hashMap.put(key, 1);
} else {
hashMap.put(key, value + 1);
}
}
countDownLatch.countDown();
};
// 两个线程操作同一个key,可能会出现覆盖的现象,导致key的值小于10
new Thread(runnable).start();
new Thread(runnable).start();
countDownLatch.await();
System.out.println(hashMap);
}
public static void main(String[] args) throws InterruptedException {
String key = "key";
Map hashMap = new ConcurrentHashMap<>();
CountDownLatch countDownLatch = new CountDownLatch(2);
Runnable runnable = () -> {
Integer oldValue, newValue;
for (int i = 0; i < 5; i++) {
while (true) {
// CAS机制:如果put失败,继续获取最新的值,然后继续尝试添加,直到put成功
oldValue = hashMap.get(key);
if (oldValue == null) {
newValue = 1;
// 如果key != newValue 则 hashMap.put(key, newValue)
if (hashMap.putIfAbsent(key, newValue) == null) {
break;
}
} else {
newValue = oldValue + 1;
// 如果key对应的当前值是oldValue,则替换为newValue,返回true。否则不替换,返回false
if (hashMap.replace(key, oldValue, newValue)) {
break;
}
}
}
}
countDownLatch.countDown();
};
new Thread(runnable).start();
new Thread(runnable).start();
countDownLatch.await();
System.out.println(hashMap);
}
public static void main(String[] args) {
String key = “key”;
Map
public static void main(String[] args) throws InterruptedException {
String key = "key";
Map hashMap = new ConcurrentHashMap<>();
CountDownLatch countDownLatch = new CountDownLatch(2);
Runnable runnable = () -> {
AtomicInteger oldValue = null;
for (int i = 0; i < 5; i++) {
oldValue = hashMap.get(key);
if (oldValue == null) {
System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t if");
AtomicInteger initValue = new AtomicInteger(0);
oldValue = hashMap.putIfAbsent(key, initValue);
if (oldValue == null) {
oldValue = initValue;
}
}
oldValue.incrementAndGet();
}
countDownLatch.countDown();
};
new Thread(runnable).start();
new Thread(runnable).start();
countDownLatch.await();
System.out.println(hashMap);
}
彻头彻尾理解 ConcurrentHashMap: https://blog.csdn.net/justloveyou_/article/details/72783008