如何对一个Map的不同的Key进行同步?

一般来说,我们要对一个对象进行多线程同步,那么只要进行 synchronized(object) 就可以了。

对于一个Map对象来说,也只要synchronized(map) 就可以了。但是这样就对整个map都进行了同步,如何对不同的key进行区分呢?

注意,这里的synchronized(object) 同步的时候,必须是 object1==object2, 而不是 object1.equals(object2)。 而Map判断是否相同的key是用 equals 方法的。无法达到要求。

范例代码如下:


String key1 = "abc";
String key2 = "a" + "bc";

Thread1: get_from_cache(key1);
Thread2: get_from_cache(key2);

public Object get_from_cache(Object key) {
    Object value = map.get(key);
    if (value == null) {
        synchronized(key) {
            value = map.get(key);
            if (value == null) {  // double check
                value = get_from_other(key);
                map.put(key, value);
            }
        }
    }
    return value;
}

public Object get_from_other(key) {
    return ...
}
2个Thread并不能保证在cache都得不到的情况下,会重复get 2次。 里面的double check 失效。


这里,我采用了一个代理对象,先将 key 映射成一个固定的对象,来达到 key1==key2.

代理类如下:

public class VolatileObject<T> {

	private volatile T value;

	public T get() {
		return value;
	}

	public void set(T value) {
		this.value = value;
	}

}

访问方法如下:

String key1 = "abc";
String key2 = "a" + "bc";

Thread1: get_from_cache(key1);
Thread1: get_from_cache(key2);

ConcurrentMap<Object, VolatileObject> cache = new ConcurrentHashMap();

public Object get_from_cache(Object key) {
    VolatileObject ref = cache.get(key);
    if (ref == null) {
        ref = new VolatileObject();
        VolatileObject old = cache.putIfAbsent(key, ref);
        if (old != null) ref = old; // 将 key 变成同一对象 ref
    }

    Object value = ref.get(key);
    if (value == null) {
        synchronized(ref) {
            value = ref.get(key); 
            if (value == null) { // double check
                value = get_from_other(key);
                ref.put(value);
            }
        }
    }
    return value;
}

public Object get_from_other(key) {
    return ...
}

你可能感兴趣的:(java,Concurrent)