I have a question about using maps in multithreaded application. Suppose we have such scenario:
- Thread receives json data as
List<Map<String, Object>>
which is deserialized by Jackson Json. - This thread modifies received maps.
- And then puts list into blocking queue to be consumed by another thread.
As you can see, map is modified only by single thread, but then it "becomes" read-only (nothing chagnes, just not modified anymore) and passed to another thread. Next, when I looked into implementations of HasMap
(also TreeMap
) and ConcurrentHashMap
, the latter has volatile
fields while first two isn't. So, which implementation of Map
should I use in this case? Does ConcurrentHashMap
is overkill choice or it must be used due to inter-thread transfer?
My simple tests shows that I can use HashMap/TreeMap
when they are modified synchronously and it works, but my conclusion or my test code may be wrong:
def map = new TreeMap() // or HashMap
def start = new CountDownLatch(1)
def threads = (1..5)
println("Threads: " + threads)
def created = new CountDownLatch(threads.size())
def completed = new CountDownLatch(threads.size())
threads.each {i ->
new Thread({
def from = i * 10
def to = from + 10
def local = (from..to)
println(Thread.currentThread().name + " " + local)
created.countDown()
start.await()
println('Mutating by ' + local)
local.each {number ->
synchronized (map) {
map.put(number, ThreadLocalRandom.current().nextInt())
}
println(Thread.currentThread().name + ' added ' + number + ': ' + map.keySet())
}
println 'Done: ' + Thread.currentThread().name
completed.countDown()
}).start()
}
created.await()
start.countDown()
completed.await()
println('Completed:')
map.each { e ->
println('' + e.key + ': ' + e.value)
}
Main thread spawns 5 child threads which updates common map synchronously, when they complete main thread successfully sees all updates by child threads.
The
java.util.concurrent
classes have special guarantees regarding sequencing:This means that you are free to use any kind of mutable object and manipulate it as you wish, then put it into the queue. When it's retrieved, all of the manipulations you've applied will be visible.
(Note more generally that the kind of test you demonstrated can only prove lack of safety; in most real-world cases, unsynchronized code works fine 99% of the time. It's that last 1% that bites you.)