Skip to content

Commit

Permalink
docs: update reader-writer lock
Browse files Browse the repository at this point in the history
  • Loading branch information
iluwatar committed May 18, 2024
1 parent a766f18 commit cfbb5af
Showing 1 changed file with 56 additions and 113 deletions.
169 changes: 56 additions & 113 deletions reader-writer-lock/README.md
Original file line number Diff line number Diff line change
@@ -1,54 +1,50 @@
---
title: Reader Writer Lock
title: Reader-Writer Lock
category: Concurrency
language: en
tag:
- Performance
- Asynchronous
- Concurrency
- Performance
- Resource management
- Synchronization
- Thread management
---

## Also known as

* Shared-Exclusive Lock

## Intent

Regular lock does not distinguish the ‘read lock’ and ‘write lock’, as when access the data structure patterns
consists of many threads reading the data, each thread will have to lock it which produces unnecessary serialization.
The existence of reader-writer lock resolves this issue as it is well known as
“multiple concurrent readers, single writer locks”, used to consist of multiple threads reading the data concurrently
and allow only one thread to write or modify the data. All others (readers or writers) will be blocked while the writer
is modifying or writing the data and unblocked until the writer finishes writing.
Allow concurrent access to a shared resource for read operations while limiting access for write operations to ensure data consistency.

## Explanation

Real world example

> Consider if we obtain a database for bank accounts. If Alice wants to transfer from account1 to account2, at the
> same time Bob transfer money from account2 to account3. Alice will first read the totals of account1 and account2. Then, Bob's
> transaction executed completely. Alice is now working with outdated values, therefore, the total amount in account2
> would be incorrect. With transactions, Bob would have to wait until Alice finishes her process of the accounts.


> Imagine a library where patrons frequently come to read books. The library allows multiple people to read the same book simultaneously without any issues. However, when someone wants to update or correct the content of the book, the library ensures that no one else is reading the book during the update process. This ensures that the book's content remains consistent and accurate, similar to how a Reader-Writer Lock allows multiple threads to read a shared resource concurrently while restricting write access to one thread at a time.
In plain words

> Reader-writer lock enables either multiple readers or single writer to hold the lock at any given time.
> The Reader-Writer Lock design pattern allows concurrent read access to a shared resource while ensuring exclusive write access to maintain data consistency.
Wikipedia says
> In computer science, a readers–writer (single-writer lock, a multi-reader lock, a push lock, or an MRSW lock)
> is a synchronization primitive that solves one of the readers–writers problems.
Wikipedia says

> In computer science, a readers–writer (single-writer lock, a multi-reader lock, a push lock, or an MRSW lock) is a synchronization primitive that solves one of the readers–writers problems.
**Programmatic Example**

In our programmatic example, we demonstrate the implementation of the access to either reader or writer.
We first create a `Reader` class which read when it acquired the read lock. It creates the reader and simulate the read operation.
The Reader-Writer Lock design pattern allows concurrent read access to a shared resource while ensuring exclusive write access to maintain data consistency. This pattern is particularly useful in scenarios where read operations are more frequent than write operations.

First, we have the `Reader` class. This class represents a reader that can read a shared resource when it acquires the read lock. The `run` method locks the read lock, performs the read operation, and then unlocks the read lock.

```java
@Slf4j
public class Reader implements Runnable {

private Lock readLock;

private Lock readLock;
private String name;

private long readingTime;

public Reader(String name, Lock readLock, long readingTime) {
Expand All @@ -57,11 +53,6 @@ public class Reader implements Runnable {
this.readingTime = readingTime;
}

public Reader(String name, Lock readLock) {
this(name, readLock, 250L);
}


@Override
public void run() {
readLock.lock();
Expand All @@ -81,25 +72,17 @@ public class Reader implements Runnable {
LOGGER.info("{} finish after reading {}ms", name, readingTime);
}
}


```

Next, we have the `Writer` class. This class represents a writer that can write to a shared resource when it acquires the write lock. Similar to the `Reader` class, the `run` method locks the write lock, performs the write operation, and then unlocks the write lock.

In the `Writer` class, we operate write when it acquired the writer lock. It follows the similar process as the `Reader` class
which creates the writer and simulate the write operation.
```java

public class Writer implements Runnable {

private final Lock writeLock;
private final String name;
private final long writingTime;

public Writer(String name, Lock writeLock) {
this(name, writeLock, 250L);
}

public Writer(String name, Lock writeLock, long writingTime) {
this.name = name;
this.writeLock = writeLock;
Expand All @@ -118,36 +101,26 @@ public class Writer implements Runnable {
writeLock.unlock();
}
}

public void write() throws InterruptedException {
LOGGER.info("{} begin", name);
Thread.sleep(writingTime);
LOGGER.info("{} finished after writing {}ms", name, writingTime);
}
}



```


Now, in the `ReadWriteLock` class which would take the responsibilities to control the access for either the reader or the writer.
In the `ReadLock` class, it restricts that if there's no writer that gets the lock, then multiple readers could be access concurrently.
In the `WriteLock` class, it restricts that only one writer could be accessed.
Finally, we have the `ReadWriteLock` class. This class controls the access to the shared resource. It allows multiple readers to access the resource concurrently if there's no writer that has the lock. On the other hand, it ensures that only one writer can access the resource at a time.

```java

public class ReadWriteLock implements ReaderWriterLock {

private final Object readerMutex = new Object();
private int currentReaderCount;


private final Set<Object> globalMutex = new HashSet<>();
private final ReadLock readerLock = new ReadLock();
private final WriteLock writerLock = new WriteLock();


public Lock readLock() {
return readerLock;
}
Expand All @@ -164,86 +137,56 @@ public class ReadWriteLock implements ReaderWriterLock {
return globalMutex.isEmpty();
}


private class ReadLock implements Lock {

@Override
public void lock() {
synchronized (readerMutex) {
currentReaderCount++;
if (currentReaderCount == 1) {
acquireForReaders();
}
}
}

@Override
public void unlock() {
synchronized (readerMutex) {
currentReaderCount--;

if (currentReaderCount == 0) {
synchronized (globalMutex) {
globalMutex.remove(this);
globalMutex.notifyAll();
}
}
}

}
// Implementation of the read lock
}


private class WriteLock implements Lock {

@Override
public void lock() {
synchronized (globalMutex) {
while (!isLockFree()) {
try {
globalMutex.wait();
} catch (InterruptedException e) {
LOGGER.info("InterruptedException while waiting for globalMutex to begin writing", e);
Thread.currentThread().interrupt();
}
}
globalMutex.add(this);
}
}

@Override
public void unlock() {
synchronized (globalMutex) {
globalMutex.remove(this);
globalMutex.notifyAll();
}
}

// Implementation of the write lock
}
}
```

In the `App` class, we create a fixed thread pool and submit `Reader` and `Writer` tasks to it. The `Reader` tasks acquire the read lock to read the shared resource, and the `Writer` tasks acquire the write lock to write to the shared resource.

## Class diagram

![Reader-Writer Lock](./etc/reader-writer-lock.png "Reader-Writer Lock")

## Applicability

```
* Use when you need to manage concurrent read and write access to a shared resource.
* Suitable for systems with more frequent read operations than write operations.
* Ideal for situations where read operations can proceed concurrently but write operations require exclusive access.

## Known Uses

## Class diagram
![alt text](./etc/reader-writer-lock.png "Reader writer lock")
* Database management systems to allow multiple transactions to read data simultaneously while ensuring exclusive access for data modifications.
* Filesystems to manage concurrent read and write operations on files.
* In-memory caches where read access is predominant over write access.
* [ReentrantReadWriteLock in Java](https://docs.oracle.com/en/java/javase/17/docs//api/java.base/java/util/concurrent/locks/ReentrantReadWriteLock.html)

## Applicability
## Consequences

Use the Reader-writer lock when:
* You need to increase the performance of resource synchronize for multiple thread, in particularly there are mixed read/write operations.
* In the bank transaction system, you want to ensure when two users are transacting the same account, one people will wait until the other finishes.
Benefits:

* Improves performance in scenarios with a high ratio of read operations to write operations.
* Reduces contention and increases concurrency for read operations.

Trade-offs:

* More complex to implement compared to simple locks.
* Potential for starvation where write operations could be delayed indefinitely if read operations continue to occur.
* Requires careful handling of thread management and synchronization to avoid deadlocks and ensure data consistency.

## Related Patterns

## Credits
* [Lockable Object](https://java-design-patterns.com/patterns/lockable-object/): Both patterns deal with managing access to shared resources, but Reader-Writer Lock allows higher concurrency for read operations.

* [Readers–writer lock](https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock)
* [Readers–writers_problem](https://en.wikipedia.org/wiki/Readers%E2%80%93writers_problem)
## Credits

* [Concurrent Programming in Java : Design Principles and Patterns](https://amzn.to/4dIBqxL)
* [Java Concurrency in Practice](https://amzn.to/4aRMruW)
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
* [Readers–writer lock - Wikipedia](https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock)
* [Readers–writers_problem - Wikipedia](https://en.wikipedia.org/wiki/Readers%E2%80%93writers_problem)

0 comments on commit cfbb5af

Please sign in to comment.