4

我有一个类,其中包含两个带有一些数据的哈希图。我想每晚更新/重新加载地图中的数据(使用 Quartz 作业),并希望在数据刷新过程中锁定所有其他线程以防止读取。

 public class A {

    private Map<String, Object> someMap1 = new ConcurrentHashMap<String, Object>();
    private Map<String, Object> someMap2 = new ConcurrentHashMap<String, Object>();

    public void reloadData() {
        someMap1.clear();
        someMap2.clear();

        // read new data here and re-fill the maps
        ...
    }

    public Object getDataFromMap(String key) {
        // do some logic here and return data from map
        return someObj;
    }
 }

getDataFromMap() 方法应该可供所有“读取器”线程访问,而不会在数据刷新未进行时出现任何阻塞。

另一方面,reloadData() 方法应该等待所有“读取器”完成,然后阻止地图读取并重新加载数据。

reloadData() 的“同步”修饰符不是解决方案,因为它会阻止所有类和所有“阅读器”(如果它们在 getDataFromMap() 逻辑中正在进行)。

4

3 回答 3

6

您的要求完全适合ReentrantReadWriteLock

    private Map<String, Object> someMap1 = new ConcurrentHashMap<String, Object>();
    private Map<String, Object> someMap2 = new ConcurrentHashMap<String, Object>();
    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private final Lock r = rwl.readLock();
    private final Lock w = rwl.writeLock();

    public Object getDataFromMap(String key) {
        r.lock();
        try {
            // do some logic here and return data from map
            return someObj;
        } finally {
            r.unlock();
        }
    }

    public void reloadData() {
        w.lock();
        try {
            someMap1.clear();
            someMap2.clear();
            // read new data here and re-fill the maps
        } finally {
            w.unlock();
        }
    }
于 2012-10-11T17:31:10.613 回答
5

在java中使用并发包中的Lock类。它有 ReenterantReadWriteLock。

于 2012-10-11T17:23:03.730 回答
1

与其那样做,不如:

public class A {
    private volatile Map<String, Object> someMap1 = new HashMap<String, Object>();
    private volatile Map<String, Object> someMap2 = new HashMap<String, Object>();

    public void reloadData() {
        Map<String, Object> newMap1 = new HashMap<String, Object>();
        Map<String, Object> newMap2 = new HashMap<String, Object>();

        // read new data here and fill the new maps

        someMap1 = newMap1;
        someMap2 = newMap2;
    }

    public Object getDataFromMap(String key) {
        // do some logic here and return data from map
        return someObj;
    }
}

这样,在重新加载期间无需阻止阅读器。缺点是在重新加载期间,您将同时在内存中拥有地图的旧副本和新副本,这将增加您的最大内存消耗。

我上面给出的实现也可能包含一个微妙的竞争条件,因为这两个映射不是原子更新的,所以读者可以看到旧版本someMap2但新版本的someMap1. 如果这是一个问题,可以通过多种方式相当容易地解决。

于 2012-10-11T17:55:51.173 回答