Java supports multithreading, which allows multiple threads to execute concurrently. When threads share data or objects, improper synchronization can lead to problems such as thread interference and memory consistency errors.
- Thread interference occurs when multiple threads access and modify shared data at the same time, causing unexpected results.
- Memory consistency errors occur when changes made by one thread are not properly visible to another thread.
Thread Interference Error
Thread interference occurs when two or more threads access and modify the same shared data, and their operations overlap in an unexpected order.
An operation such as i++ may involve multiple steps:
- Read the current value of i.
- Increment the value.
- Write the updated value back to memory.
If another thread modifies i between these steps, one thread's update may overwrite the other thread's update.
class Counter {
static int count = 0;
void increment()
{
for (int i = 0; i < 1000; i++) {
count++;
}
}
void decrement()
{
for (int i = 0; i < 1000; i++) {
count--;
}
}
}
public class ThreadInterferenceDemo {
public static void main(String[] args)
throws InterruptedException
{
Counter counter = new Counter();
Thread t1 = new Thread(() -> counter.increment());
Thread t2 = new Thread(() -> counter.decrement());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Final count: " + Counter.count);
}
}
Output
Final count: 0
Explanation: Both threads modify the same count variable. Since count++ and count-- are not atomic operations, their individual read-modify-write steps can overlap. As a result, updates may be lost and the final value may not always be the expected 0.
Note: The output may vary because both threads access and modify the same shared variable concurrently. In some executions, the final value may differ from 0 due to thread interference.
How to Avoid Thread Interference
Thread interference can be prevented by ensuring that access to shared data is properly controlled.
Common approaches include:
- Synchronization: Allows only one thread at a time to execute a critical section.
- Atomic variables: Classes such as AtomicInteger provide atomic operations.
- Immutable objects: Objects whose state cannot be changed after creation reduce shared-state problems.
- Proper access control: Restricting how multiple threads access shared data can prevent conflicting modifications.
Memory Consistency Error
A memory consistency error occurs when changes made by one thread to shared data are not immediately visible to another thread. Java provides concurrency mechanisms to ensure proper visibility and ordering of changes between threads.
- It commonly occurs when multiple threads access and modify shared variables without proper synchronization.
- The happens-before relationship helps ensure that updates made by one thread are visible to other threads.
class SharedData {
static boolean ready = false;
static int value = 0;
}
public class MemoryConsistencyDemo {
public static void main(String[] args)
throws InterruptedException
{
Thread writer = new Thread(() -> {
SharedData.value = 100;
SharedData.ready = true;
});
Thread reader = new Thread(() -> {
while (!SharedData.ready) {
// Wait until the value is available
}
System.out.println("Value: "
+ SharedData.value);
});
writer.start();
reader.start();
writer.join();
reader.join();
}
}
Output
Value: 100
Explanation: The writer thread modifies value and ready, while the reader thread checks ready and then reads value. Without a proper synchronization mechanism, the Java Memory Model does not guarantee the required visibility between these operations.
Thread Interference Vs Memory Consistency Errors
| Thread Interference | Memory Consistency Error |
|---|---|
| Occurs when multiple threads interleave operations on shared data. | Occurs when changes made by one thread are not properly visible to another thread. |
| Mainly involves conflicting updates to shared state. | Mainly involves visibility and ordering of shared data. |
| Can cause updates to be lost or overwritten. | Can cause a thread to observe stale or unexpected data. |
| Synchronization and atomic operations can help prevent it. | Happens-before relationships, synchronization, volatile, and thread coordination can help ensure visibility. |