Java Concurrency: thread safe modification of values ​​in maps

I'm having problems with concurrency and maps in Java. Basically I have multiple threads that use (read and modify) their own maps, however each of these maps is part of a larger map that is read and modified by a different thread:

My main method creates all streams, streams create corresponding maps, which are then put into the "main" map:

Map<String, MyObject> mainMap = new HashMap<String, Integer>();
FirstThread t1 = new FirstThread();
mainMap.putAll(t1.getMap());
t1.start();
SecondThread t2 = new SecondThread();
mainMap.putAll(t2.getMap());
t2.start();
ThirdThread t3 = new ThirdThread(mainMap);
t3.start();

      

The problem I am facing now is that the third (main) thread sees arbitrary values ​​in the map, depending on when one or both of the other threads update their "own" elements. However, I have to ensure that the third thread can iterate over and use the map values ​​without fear that some of what I read is "old":

FirstThread (analogue of SecondThread):

for (MyObject o : map.values()) {
    o.setNewValue(getNewValue());
}

      

ThirdThread:

for (MyObject o : map.values()) {
    doSomethingWith(o.getNewValue());
}

      

Any ideas? I decided to use a globally accessible (static end object via a static class) that will sync on every thread when the map needs to be changed. Or are there specific map implementations that assess this specific problem that I could use?

Thanks in advance!

Edit: As suggested by @Pyranja it would be possible to synchronize the getNewValue () method. However, I forgot to mention that I am actually trying to do something along the lines of transactions, where t1 and t2 change multiple values ​​before / after t3 works with the specified values. t3 is implemented in such a way that doSomethingWith () doesn't actually do anything to the value if it hasn't changed.

+3


source to share


3 answers


For synchronization at a higher level than individual value objects, you need locks to handle synchronization between different threads. One way to do this without changing too much code is ReadWriteLock . Thread 1 and Thread 2 are writers, Thread 3 is a reader.

You can do this with two locks or one. I've sketched out below, doing it with one lock, two writer threads, and one reader thread, without worrying about what happens to the exception during a data update (i.e. rollback transactions ...).

All of the above sounds like a classic producer-consumer scenario. You should be using something like BlockingQueue for cross-thread communication as described in this question .



Other things you might want to change as well, such as using Runnable instead of continuing with Thread .

private static final class Value {

    public void update() {

    }

}

private static final class Key {

}

private final class MyReaderThread extends Thread {

    private final Map<Key, Value> allValues;

    public MyReaderThread(Map<Key, Value> allValues) {
        this.allValues = allValues;
    }

    @Override
    public void run() {
        while (!isInterrupted()) {
            readData();
        }
    }

    private void readData() {
        readLock.lock();
        try {
            for (Value value : allValues.values()) {
                // Do something
            }
        }
        finally {
            readLock.unlock();
        }

    }
}

private final class WriterThread extends Thread {

    private final Map<Key, Value> data = new HashMap<Key, Value>();

    @Override
    public void run() {
        while (!isInterrupted()) {
            writeData();
        }
    }

    private void writeData() {
        writeLock.lock();

        try {
            for (Value value : data.values()) {
                value.update();
            }
        }
        finally {
            writeLock.unlock();
        }
    }
}

private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

private final ReadLock readLock;
private final WriteLock writeLock;

public Thing() {
    readLock = lock.readLock();
    writeLock = lock.writeLock();
}

public void doStuff() {
    WriterThread thread1 = new WriterThread();
    WriterThread thread2 = new WriterThread();

    Map<Key, Value> allValues = new HashMap<Key, Value>();
    allValues.putAll(thread1.data);
    allValues.putAll(thread2.data);
    MyReaderThread thread3 = new MyReaderThread(allValues);

    thread1.start();
    thread2.start();
    thread3.start();
}

      

+4


source


ConcurrentHashMap

from java.util.concurrent

- a streaming Map implementation that provides a much higher degree of concurrency than synchronizedMap. Simple reads can almost always be done in parallel, concurrent reads and writes are usually done in parallel, and multiple concurrent writes can often be done in parallel. (The class ConcurrentReaderHashMap

offers similar parallelism for multiple reads, but only allows one active write.) ConcurrentHashMap

Is intended to optimize lookups.



+2


source


Your example code may be misleading. In the first example, you are creating HashMap<String,Integer>

, but the second part is iterating over the map values, which in this case are MyObject

. The key to synchronization is to understand where and what state is mutable .

An Integer

is immutable. It is free to use (but link toInteger

changed - it must be safely published and / or synchronized). But your code example assumes that the cards are populated with mutable instances MyObject

.

Considering that the entries in the map (keywords -> MyObject

links) do not change in any stream and that all maps are created and published safely before any stream starts, this would be sufficient in my opinion to synchronize the modification MyObject

. For example:.

public class MyObject {
   private Object value;

   synchronized Object getNewValue() {
      return value;
   }

   synchronized void setNewValue(final Object newValue) {
      this.value = newValue;
   }
}

      

If my assumptions are wrong, please clarify your question / code example, and also consider @jacobm's comment and @ Alex's answer.

+2


source







All Articles