diff --git a/pom.xml b/pom.xml index 9fa150dd4..14ac2002d 100644 --- a/pom.xml +++ b/pom.xml @@ -60,6 +60,7 @@ intercepting-filter producer-consumer poison-pill + reader-writer-lock lazy-loading service-layer specification diff --git a/reader-writer-lock/etc/reader-writer-lock.png b/reader-writer-lock/etc/reader-writer-lock.png new file mode 100644 index 000000000..06207b502 Binary files /dev/null and b/reader-writer-lock/etc/reader-writer-lock.png differ diff --git a/reader-writer-lock/etc/reader-writer-lock.ucls b/reader-writer-lock/etc/reader-writer-lock.ucls new file mode 100644 index 000000000..18ffb8ccb --- /dev/null +++ b/reader-writer-lock/etc/reader-writer-lock.ucls @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/reader-writer-lock/index.md b/reader-writer-lock/index.md new file mode 100644 index 000000000..75f57a4cd --- /dev/null +++ b/reader-writer-lock/index.md @@ -0,0 +1,29 @@ +--- +layout: pattern +title: Producer Consumer +folder: reader writer lock +permalink: /patterns/reader-writer-lock/ +categories: Concurrent +tags: +- Java +--- + +**Intent:** + +Suppose we have a shared memory area with the basic constraints detailed above. It is possible to protect the shared data behind a mutual exclusion mutex, in which case no two threads can access the data at the same time. However, this solution is suboptimal, because it is possible that a reader R1 might have the lock, and then another reader R2 requests access. It would be foolish for R2 to wait until R1 was done before starting its own read operation; instead, R2 should start right away. This is the motivation for the Reader Writer Lock pattern. + +![alt text](./etc/reader-writer-lock.png "Reader writer lock") + +**Applicability:** + +Application need to increase the performance of resource synchronize for multiple thread, in particularly there are mixed read/write operations. + +**Real world examples:** + +* [Java Reader Writer Lock](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReadWriteLock.html) + +**Credits** + +* [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) \ No newline at end of file diff --git a/reader-writer-lock/pom.xml b/reader-writer-lock/pom.xml new file mode 100644 index 000000000..f1553a4d1 --- /dev/null +++ b/reader-writer-lock/pom.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.9.0-SNAPSHOT + + reader-writer-lock + + + junit + junit + test + + + diff --git a/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/App.java b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/App.java new file mode 100644 index 000000000..aa36ef47c --- /dev/null +++ b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/App.java @@ -0,0 +1,82 @@ +package com.iluwatar.reader.writer.lock; + +import java.util.Random; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.IntStream; + +/** + * Reader writer lock is a synchronization primitive that solves one of the readers–writers + * problems. An RW lock allows concurrent access for read-only operations, while write operations + * require exclusive access. + *

+ * Below example use two mutexes to demonstrate the concurrent access of mutilple readers and + * writers. + * + */ +public class App { + + private static Random ran = new Random(); + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + + ExecutorService es = Executors.newFixedThreadPool(1000); + ReaderWriterLock lock = new ReaderWriterLock(); + + AtomicInteger index = new AtomicInteger(0); + IntStream.range(0, 100).forEach(i -> { + Runnable task = null; + if (ran.nextFloat() <= 0.6) { + task = new Runnable() { + @Override + public void run() { + Lock writeLock = lock.writeLock(); + writeLock.lock(); + try { + int cur = index.getAndIncrement(); + System.out.println("Writer " + cur + " begin"); + simulateReadOrWrite(); + System.out.println("Writer " + cur + " finish"); + } finally { + writeLock.unlock(); + } + } + }; + } else { + task = new Runnable() { + + @Override + public void run() { + Lock readLock = lock.readLock(); + readLock.lock(); + try { + int cur = index.getAndIncrement(); + System.out.println("Reader " + cur + " begin"); + simulateReadOrWrite(); + System.out.println("Reader " + cur + " finish"); + + } finally { + readLock.unlock(); + } + } + }; + } + es.submit(task); + }); + + } + + private static void simulateReadOrWrite() { + try { + Thread.sleep((long) (ran.nextFloat() * 10)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } +} diff --git a/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Lock.java b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Lock.java new file mode 100644 index 000000000..7cbe47749 --- /dev/null +++ b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Lock.java @@ -0,0 +1,18 @@ +package com.iluwatar.reader.writer.lock; + +/** + * Lock interface + */ +public interface Lock { + + /** + * Try to lock, it will wait until get the lock + */ + public void lock(); + + /** + * Release lock + */ + public void unlock(); +} + diff --git a/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/ReaderWriterLock.java b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/ReaderWriterLock.java new file mode 100644 index 000000000..2d92696e3 --- /dev/null +++ b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/ReaderWriterLock.java @@ -0,0 +1,147 @@ +package com.iluwatar.reader.writer.lock; + +import java.util.HashSet; +import java.util.Set; + +/** + * Class responsible for control the access for reader or writer + */ +public class ReaderWriterLock { + + /** + * Mutex for reader + */ + private Object r = new Object(); + + /** + * Global mutex for reader or writer, use to save the holding object + */ + private Set g = new HashSet<>(); + + /** + * Current reader count + */ + private int readerCount = 0; + + private ReaderLock readLock = new ReaderLock(); + private WriterLock writeLock = new WriterLock(); + + + public Lock readLock() { + return readLock; + } + + + public Lock writeLock() { + return writeLock; + } + + + /** + * Reader Lock, can be access for more than one reader concurrently if no writer get the lock + */ + private class ReaderLock implements Lock { + + @Override + public void lock() { + + synchronized (r) { + + readerCount++; + if (readerCount == 1) { + + synchronized (g) { + + while (true) { + if (isLockFree() || isReaderOwnThisLock()) { + g.add(this); + break; + } else { + waitUninterruptely(g); + } + } + } + + } + } + } + + + @Override + public void unlock() { + + synchronized (r) { + readerCount--; + if (readerCount == 0) { + synchronized (g) { + g.remove(this); + g.notifyAll(); + } + } + } + + } + + } + + + /** + * Writer Lock, can only be accessed by one writer concurrently + */ + private class WriterLock implements Lock { + + @Override + public void lock() { + + synchronized (g) { + + while (true) { + + if (isLockFree()) { + g.add(this); + break; + } else if (isWriterOwnThisLock()) { + waitUninterruptely(g); + } else if (isReaderOwnThisLock()) { + waitUninterruptely(g); + } else { + throw new RuntimeException("it should never reach here"); + } + } + } + } + + + @Override + public void unlock() { + + synchronized (g) { + g.remove(this); + g.notifyAll(); + } + } + } + + private boolean isWriterOwnThisLock() { + return g.contains(writeLock); + } + + private boolean isReaderOwnThisLock() { + return g.contains(readLock); + } + + private boolean isLockFree() { + return g.isEmpty(); + } + + + + private void waitUninterruptely(Object o) { + try { + o.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } +} + diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/AppTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/AppTest.java new file mode 100644 index 000000000..95656b14e --- /dev/null +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/AppTest.java @@ -0,0 +1,20 @@ +package com.iluwatar.reader.writer.lock; + +import org.junit.Test; + +import com.iluwatar.reader.writer.lock.App; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() throws Exception { + String[] args = {}; + App.main(args); + + } +}