Skip to content

Instantly share code, notes, and snippets.

@valarpirai
Created April 27, 2024 10:03
Show Gist options
  • Save valarpirai/f2220158f7e8ac8ec8beccac62cf435a to your computer and use it in GitHub Desktop.
Save valarpirai/f2220158f7e8ac8ec8beccac62cf435a to your computer and use it in GitHub Desktop.
Java Thread Race Condition example

Race Condition simulation

Here, we have an Increment class which stores a variable count and a function that increments the count. In the RaceConditionsExample, we’re starting a thousand threads, each of which will invoke the increment() method. Finally, we’re waiting for all the threads to finish executing and then print out the value of the count variable.

If you run the code multiple times, you’ll notice that sometimes the final value of count is less than 1,000. To understand why this happens, let’s take two threads, Thread-x and Thread-y, as examples. The threads can execute the read write operation in any order. So, there will be a case when the order of execution is as follows.

Thread-x: Reads this.count (which is 0)
Thread-y: Reads this.count (which is 0)
Thread-x: Increments this.count by 1
Thread-y: Increments this.count by 1
Thread-x: Updates this.count (which becomes 1)
Thread-y: Updates this.count (which becomes 1)

In this case, the final value of the count variable is 1 and not 2. This is because both the threads are reading the count variable before any of them can update the value. This is known as a race condition. More specifically, a “read-modify-write” race condition.

Example.java

Demonstrates modifying(incrementing) a shared storage/variable without synchronization leads to race condition (inconsitent/unexpected state)

ExampleSynchronization.java

Shows how to make avoid race conditions using synchronized keyword in java

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Example {
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newCachedThreadPool();
int max_thread = 1000;
Increment eg = new Increment();
// CountDownLatch is used for waiting machanism
CountDownLatch latch = new CountDownLatch(max_thread);
for (int i = 0; i < max_thread; i++) {
executor.submit(() -> {
eg.increment(); // This will call normal method
latch.countDown();
});
}
latch.await();
System.out.println(eg.getCount());
executor.shutdown();
}
}
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExampleSynchronization {
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newCachedThreadPool();
int max_thread = 1000;
Increment eg = new Increment();
// CountDownLatch is used for waiting machanism
CountDownLatch latch = new CountDownLatch(max_thread);
for (int i = 0; i < max_thread; i++) {
executor.submit(() -> {
eg.syn_increment(); // This will call synchronized method
latch.countDown();
});
}
latch.await();
System.out.println(eg.getCount());
executor.shutdown();
}
}
public class Increment {
private int count = 0;
public synchronized void syn_increment() {
count++;
}
public void increment() {
count++;
}
public int getCount() {
return this.count;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment