四种线程安全的 hashmap

1.hashtable

采用synchronized方法上加锁,使用阻塞同步,效率低。

2.collections.synchronizedMap(map)

也是采用synchronized方法上加锁,使用阻塞同步,效率低。

3.CopyOnWriteMap (读写分离思想)

(java本身并没有提供CopyOnWriteMap,但是我们可以自己实现一个,代码见下)

采用 写时复制 的操作

写时复制:

当对容器进行增加,删除,修改操作时,不是直接操作容器,而是先将当前容器copy,复制出一个新的容器,在对新容器进行操作,操作完成后再将指针指向这个新容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读(CopyOnWriteArrayList底层使用数组实现,而该数组是被volatile修饰),而不用对其进行加锁,当然写的时候是需要加锁的(这里采用了lock加锁),否则可能copy出了N个副本。

优点:写时不用加锁,即查询不加锁,在查询多,增删改少的情况下适合用。

缺点(问题):内存占用和数据一致性

1.内存占用

因为CopyOnWrite的写时复制机制,所有在进行写操作的时候,内存里会同时驻扎两个对象的内存,旧的对象和新的对象。如果这些对象占用的内存比较大,就有可能造成频繁的yong GC和full GC。

解决:

  1. 可以通过压缩容器中的元素的方法来减少大对象的消耗,如元素全是10进制的数字,可以考虑将其压缩成32进制或者64进制.
  2. 使用其他的并发容器,如ConcurrentHashMap。

2.数据一致性

CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性(因为在对新容器进行写操作时,其它线程可能也在操作原容器)。所有如果你希望写入的数据,马上能读到,CopyOnWrite可能无法满足要求。

 

CopyOnWriteMap的实现代码:

import java.util.Collection;
import java.util.Map;
import java.util.Set;

public class CopyOnWriteMap implements Map, Cloneable {

    private volatile Map internalMap;

    public CopyOnWriteMap() {
	internalMap = new HashMap();

    }

    public V put(K key, V value) {
 
	synchronized (this) {
	    Map newMap = new HashMap(internalMap);

	    V val = newMap.put(key, value);
	    internalMap = newMap;

	    return val;

	}

    }

    public V get(Object key) {
	return internalMap.get(key);

    }

    public void putAll(Map newData) {
	synchronized (this) {
	    Map newMap = new HashMap(internalMap);

	    newMap.putAll(newData);

	    internalMap = newMap;

	}

    }

}

4.ConcurrentHashMap

采用锁分段技术,减小锁的粒度,效率高

ConcurrentHashMap中是一次锁住一个桶。

ConcurrentHashMap默认将hash表分为16个桶,诸如get,put,remove等常用操作只锁当前需要用到的桶。

这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。上面说到的16个线程指的是写线程,而读操作大部分时候都不需要用到锁。只有在size等操作时才需要锁住整个hash表。

 

你可能感兴趣的:(并发,java,线程安全的map)