13

I'd like to clarify something about ConcurrentHashMap vs ConcurrentSkipListMap based on the API documentation.

From my understanding ConcurrentHashMap gaurantees thread safety for insertions by multiple threads. So if you have a map that will only be populated concurrently by multiple threads then there are no issues. The API however goes on to suggest that it does not gaurantee locking for retrieval so you may get misleading results here?

In contrast, for the ConcurrentSkipListMap it is stated that: "Insertion, removal, update, and access operations safely execute concurrently by multiple threads". So I assume this does not have the aforementioned retrieval issue that the hash map has, but obviously this would generally come with a performance cost?

In practice, has anyone found the need to use the ConcurrentSkipListMap because of this particular behaviour, or does it generally not matter that retrievals may give an out of date view?

Bass
  • 4,977
  • 2
  • 36
  • 82
Tranquility
  • 3,061
  • 5
  • 23
  • 37

4 Answers4

11

ConcurrentHashMap

Retrievals reflect the results of the most recently completed update operations holding upon their onset. For aggregate operations such as putAll and clear, concurrent retrievals may reflect insertion or removal of only some entries.

it uses volatile semantics for get(key). In case when Thread1 calls put(key1, value1) and right after that Thread2 calls get(key1), Thread2 wouldn't wait Thread1 to finish its put, they are not synchronized with each other and Thread2 can get old associated value. But if put(key1, value1) was finished in Thread1 before Thread2 tries to get(key1) then Thread2 is guaranteed to get this update (value1).

ConcurrentSkipListMap is sorted and provides

expected average log(n) time cost for the containsKey, get, put and remove operations and their variants

ConcurrentSkipListMap isn't so fast, but is useful when you need sorted thread-safe map.

dezhik
  • 990
  • 10
  • 16
  • 1
    If you want to gaurantee getting the most up to date copy then how would you normally deal with that - is there a way to use a different data structure or instead deal with blocking on the ConcurrentHashMap? – Tranquility Jan 25 '16 at 11:43
  • 2
    Practically in all cases `ConcurrentHashMap` is good enough. For me get up to date means ability to see completed updates — and it is so in `ConcurrentHashMap`. But if you want to block all gets from the map as soon as put is started, then you have some sort of additional synchronization. You can use inefficient `Collections.synchronizedMap()` or have strictly sized array of HashMaps (like segments in ConcurrentHashMap) with accordingly associated ReadWriteLocks. Note that such blocking is almost always unnecessary and will affect performance. Sincerely I can't imagine when you can need it. – dezhik Jan 25 '16 at 12:16
4

The API however goes on to suggest that it does not gaurantee locking for retrieval so you may get misleading results here?

Interestingly enough, neither does the ConcurrentSkipListMap, infact the CSLM is completely non-blocking.

In Java 7 The CHM, for all intents and purposes, is non-blocking when executing reads. In fact, Java 8's updated CHM implementation has completely non-blocking reads.

The point here is that the CHM and CSLM have similar read semantics, the difference is time complexity.

John Vint
  • 39,695
  • 7
  • 78
  • 108
  • 1
    So why does the API say this: "Insertion, removal, update, and access operations safely execute concurrently by multiple threads." ...? – Tranquility Jan 25 '16 at 11:37
  • Because there can't occur a race condition for update operations, they are using `ReentrantLock`s for synchronization with each other. And according to the presumption that all completed updates should be visible for gets — it works right as it is said on reads. – dezhik Jan 25 '16 at 12:40
  • 2
    @Tranquility Just because an algorithm is non blocking does not mean it is thread-unsafe. There are non-blocking concurrent algorithms which completely thread safe like the `ConcurrentLinkedQueue`. The CHM uses a mix of blocking and non-blocking reads/writes to ensure thread safety. – John Vint Jan 25 '16 at 15:19
  • 1
    What about a circumstance though where for example there is some form of purchasing or bidding and one user could have purchased an item as you are reading it as still being available? Or one person may have placed a bid as you're going to make yours? Would these just be specific use cases where you need to take more care over handling the read operation? – Tranquility Jan 26 '16 at 12:31
4

From your question, you seem to have come to the conclusion that only insertions into ConcurrentHashMap are thread safe.

From my understanding ConcurrentHashMap gaurantees thread safety for insertions by multiple threads. So if you have a map that will only be populated concurrently by multiple threads then there are no issues.

How did you come to this conclusion? The first line of the documentation for ConcurrentHashMap implies that all operations are thread safe:

A hash table supporting full concurrency of retrievals and adjustable expected concurrency for updates.

Additionally, it implies that get() operations can sustain a higher level of concurrency than put() operations.

Simply put ConcurrentHashMap does not have the retrieval issue that you think it has. In most cases you should be using ConcurrentHashMap instead of ConcurrentSkipListMap since performance of ConcurrentHashMap is generally better than ConcurrentSkipListMap. You should only be using CurrentSkipListMap when you need a ConcurrentMap that has predictable iteration order or if you need the facilities of a NavigableMap.

Nam San
  • 1,145
  • 9
  • 13
0

There is a difference between the ConcurrentHashMapand ConcurrentSkipListMap in what synchronization primitives are used. ConcurrentHashMap, along with non-blocking Unsafe-based CAS operations, uses blocking synchronized construct (also know as intrinsic monitor, which uses monitorenter/monitorexit bytecodes) for accessing its "bins", while ConcurrentSkipListMap uses solely non-blocking synchronization. So, one could say that in this sense ConcurrentSkipListMap is more advanced.

This might look as a quite an insignificant difference which hardly impact the performance and everything else (especially when in Java 7 the synchronized block performance was significantly improved), but introduction of Virtual Thread in Java 19+ might change the game. According to JEP 425, a Virtual Thread might be pinned if it executes a synchronized block:

avoid frequent and long-lived pinning by revising synchronized blocks or methods that run frequently and guard potentially long I/O operations to use java.util.concurrent.locks.ReentrantLock instead.

A DZone article discusses such a replacement.

By other hand, Virtual Thread technology is not mature at this point of time and, in my opinion, there is no need for a rush to replace synchronized blocks, directly or indirectly used in your applications, with ReentrantLock, AQS or similar.

igor.zh
  • 1,410
  • 15
  • 19