Thread Interference and Memory Consistency Errors in Java
Java allows multithreading which involves concurrent execution of two or more parts of the program. It enhances CPU utilization by performing multiple tasks simultaneously. The threads communicate with each other by sharing object references and member variables.
When Two threads access the same shared memory
This communication in multithreading can cause two types of errors, if not implemented properly:
- Thread interference
- Memory inconsistency
Thread Interference Error
When multiple threads share the same memory, there is a chance that two or more different threads performing different operations on the same data interleave with each other and create inconsistent data in the memory. Threads interleave when they are performing operations with multiple steps and their sequence of steps overlap. This is called thread interference.
Even for a small operation like incrementing or decrementing the value of a variable (say i) using the statement i++ or i–, the virtual machine performs multiple steps as follows:
- Retrieve the value of i from the memory
- Increment/Decrement the value of i by 1
- Store the new incremented/decremented value of i back to the memory
If there are two threads A and B operating on the same object, A performs the increment operation and B performs decrement operation at the same time, it might lead to data inconsistency. If the initial value of i is 10. Thread A reads the value of i from the memory as 10 and increments its value to 11. Before it could store the new value to the memory, thread B reads the value of i from the memory as 10, since the memory has not been updated yet. Now, thread B decrements the value of i to 9. The new value in the memory now would be either 9 or 11, where the expected value was actually 10. Either one of the thread’s result may be lost and overwritten by the other or there could be no error at all. Being unpredictable, thread interference bugs are difficult to detect and fix.
Sequence diagram for thread interference error
In the above code the last line of the expected output should be either “i after increment 10” or “i after decrement 10”, but the actual output may vary due to the interference of the threads. Thread interference is unpredictable, try running the above code several times to find the thread interference error in some cases. The interleaving thread operations would be evidently seen.
How to avoid thread interference error
Thread interference can be avoided by making the code thread-safe through:
- Access restriction on the same object by multiple threads
- Declaring the variable as final.
- Declaring the variable as volatile.
- Creating immutable objects.
Memory Consistency Error
In multithreading, there can be possibilities that the changes made by one thread might not be visible to the other threads and they all have inconsistent views of the same shared data. This is known as memory consistency error.
Memory consistency is more of an architecture-based concept than Java-based. Accesses to main memory might not occur in the same order in which the CPU initiated them, especially for write operations which often go through hardware write buffers so that the CPU need not wait for them. CPUs guarantee that the order of writes to a single memory location is maintained from the perspective of all CPUs, even if CPUs perceive the write time of other CPUs differently than the actual time. This sometimes leads to memory inconsistency due to lack of visibility of the correct data.
Sequence diagram for memory consistency error
How to avoid memory consistency errors
Memory consistency errors can be avoided by establishing a happens-before relationship. This relationship guarantees that memory writes operation performed by one thread is visible to a read operation by any other thread on the same shared memory.
The following actions can create a happens-before relationship:
- Thread.start() – This statement makes the effects of code that led up to the creation of the new thread visible to the new thread.
- Thread.join() – This statement makes the effects of the code in the thread visible to the thread that performed the join.
Difference between thread interference and memory consistency error
|Thread interference error||Memory consistency error|
|Thread interference deals with interleaving of the execution process of two threads.||Memory inconsistency is about visibility and deals with hardware memory.|
|Thread interference can be avoided by granting exclusive access to threads, that is only one thread at a time should access the shared memory.||Memory consistency errors can be dealt with by establishing happens-before relationship which is simply a guarantee that memory writes by one specific statement are visible to another specific statement.|