Readers-Writers Problem in Java Threads Using Mutual Exclusion
The Readers-Writers problem is a classic example of synchronization challenge in computer science, which deals with a situation where many threads try to read from a shared data area while others try to write to it.
Readers: Processes that only read the shared data. They do not perform any updates. Multiple readers can read the data simultaneously without any adverse effects.
Writers: Processes that can both read and write to the shared data. A writer needs exclusive access to the data, meaning no other reader or writer can access the data while it is being modified.
The main challenge is to design a system that allows:
- Unordered List ItemMultiple readers to read the data concurrently.
- Unordered List ItemOnly one writer to access the data (read or write) at any time, with no other readers or writers accessing it.
The solution with mutual exclusion ensures that no reader is in the shared area while a write is happening, and vice versa.
- ReadersWritersProblem
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ReadersWritersProblem { // Shared data by readers and writers private static int sharedData = 0; // Lock for reader-writer access private static final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); private static final Lock readLock = readWriteLock.readLock(); private static final Lock writeLock = readWriteLock.writeLock(); static class Reader implements Runnable { @Override public void run() { for (int i = 0; i < 5; i++) { readLock.lock(); try { System.out.println(Thread.currentThread().getName() + " reads: " + sharedData); Thread.sleep(100); // Simulate read operation } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { readLock.unlock(); } } } } static class Writer implements Runnable { @Override public void run() { for (int i = 0; i < 5; i++) { writeLock.lock(); try { sharedData++; System.out.println(Thread.currentThread().getName() + " writes: " + sharedData); Thread.sleep(100); // Simulate write operation } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { writeLock.unlock(); } } } } public static void main(String[] args) { // Creating reader and writer threads Thread reader1 = new Thread(new Reader(), "Reader 1"); Thread reader2 = new Thread(new Reader(), "Reader 2"); Thread writer1 = new Thread(new Writer(), "Writer 1"); Thread writer2 = new Thread(new Writer(), "Writer 2"); // Start threads reader1.start(); reader2.start(); writer1.start(); writer2.start(); } }
Shared Data: The sharedData variable is the resource shared between readers and writers.
ReadWriteLock: ReentrantReadWriteLock is used to control access to the resource. readLock is used when a thread wants to read the data, and writeLock is used when a thread wants to write to the data.
Reader Class: Implements the reader functionality. Multiple readers can read the shared data simultaneously, as readLock allows concurrent reads.
Writer Class: Implements the writer functionality. The writeLock ensures exclusive access to the shared data, preventing other writers or readers from accessing the data while writing is in progress.
Main Method: Creates and starts reader and writer threads to simulate the concurrent access scenario.
This implementation demonstrates how to manage concurrent reads and exclusive writes to shared data in a multithreaded environment, preventing race conditions and ensuring data consistency.
The ReentrantReadWriteLock provides an efficient way to handle multiple readers by allowing them to read concurrently while still ensuring that writers have exclusive access.