To clarify, this article will discuss multithreaded read and write refers to one thread write, one or more threads read, does not include multithreaded write cases.

Imagine a scenario where one thread writes data to a Hashmap and another thread reads data to a HashMap. Would that be a problem? If so, what is the problem?

We all know there is a problem, but it may not be obvious what it is.

There are two problems.

One is memory visibility. The table where the hashMap stores data is not decorated with voliate, meaning that the reader thread may never read the latest value of the data.

The second is the instruction reorder problem, when get may get an intermediate state data, let’s look at part of the code of put method.

final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {

. if ((p = tab[i = (n – 1) hash]) == null)

tab[i] = new Node(hash, key, value, next);

.

}

As you can see, if the position specified in the TABLE array is null, a Node object is created and placed on the table array. TAB [I] = new Node(hash, key, value, next); Such operations are not atomic and can cause unexpected problems because of instruction reordering, when another thread calls get to fetch TAB [I], it gets an object that has not yet called the constructor.

If all objects in a HashMap are voliate decorated, then one thread writes and one thread reads. (Here’s my guess.) One thing that confirms this is that the Get of ConcurrentHashMap is not locked, meaning that reads and writes do not conflict in the Map structure. See comments from Sora-Zero in the section below

Atomicity issues for creating objects

Object obj = new Object(); This might be a little confusing in the case of multithreading and getting an uninitialized object, but here’s a quick explanation. The above Java statement is divided into four steps:

Allocate a space on the stack for obJ references

Create an Object in the JVM heap. Note that this is just allocating space and no constructor is called

Initialize the object created in step 2, that is, call its constructor

Obj in the stack points to objects in the heap

The above steps also seem to be ok, after all, the object created will not be referenced until the constructor is called.

The problem is that the JVM reorders instructions, so step 4 May be executed before step 3, and another thread will read objects that have no constructors yet executed, causing unknown problems. JVM reordering only guarantees consistency of results in a single thread before and after reordering.

Note that the assignment operations referenced in Java must be atomic, such as a=b in cases where both a and B are objects for both 32-bit and 64-bit JVMS. But if a and B are long or double atoms, then a= B may not be atomic on a 32-bit JVM (depending on the JVM implementation), but may be split into two 32-bit operations. But for voliate’s long,double, the assignment is atomic.

Specific can see https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.7 here

Read and write consistency in the database

Jump out of hashMap and use MVCC to avoid read/write locks in the database. That is, if MVCC is not used, the database should have read/write locks, so why do the database have read/write locks? The reason is that the write operation is not atomic. If the read/write lock or MVCC is not added, intermediate data may be read. The HBase write process is as follows:

1. Obtain the row lock

2. Open MVCC

3. Write to the memory buffer

4. Write to append log

5. Release the row lock

6.flush log

7. End of MVCC (then visible to read)

Imagine if there were no 2,7, and no read/write lock, then at step 3 another thread could read the data. If there is a problem after 3, the data is actually a write failure. That is, other threads have read data that doesn’t exist.

Similarly, in mysql, if you do not use MVCC or read/write locks, the data in a transaction can be read before the transaction is committed. If you use read/write locks, a transaction will lock the data in the changed data, and other read operations will block until the transaction commits. Therefore, in most cases, the database uses MVCC mechanism to implement non-locking reads.