4

I have a class which contains two Hashmaps with some data. I would like to update/reload data in the maps on a nightly basis (using Quartz job) and want to lock all other threads from reading during data refresh process.

 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;
    }
 }

The getDataFromMap() method should be accessible for all 'reader' threads without any blocking in case of data refresh not in progress.

On the other hand, the reloadData() method should wait for all 'readers' to complete and then block the maps from reading and reload the data.

'synchronized' modifier for reloadData() is not a solutions as it blocks all class and all 'readers' if they are in progress in getDataFromMap() logic.

Furqan Safdar
  • 16,260
  • 13
  • 59
  • 93
ovay
  • 43
  • 3

3 Answers3

6

Your requirement perfectly suits for 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();
        }
    }
Amit Deshpande
  • 19,001
  • 4
  • 46
  • 72
5

Use the Lock class in concurrent package in java. It has ReenterantReadWriteLock.

Jacob Schoen
  • 14,034
  • 15
  • 82
  • 102
Masood_mj
  • 1,144
  • 12
  • 25
1

Rather than doing it like that, how about:

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;
    }
}

That way, there is no need to block readers during the reload. The disadvantage is that during a reload, you will have the old and new copies of the maps in memory at the same time, which will increase your maximum memory consumption.

The implementation i give above may also contain a subtle race condition, because the two maps are not updated atomically, so readers could see the old version of someMap2 but the new version of someMap1. If that's a problem, it can be solved fairly easily in a number of ways.

Tom Anderson
  • 46,189
  • 17
  • 92
  • 133