From 3731d26f6d0ac7e89c1504e6527587df07292c5f Mon Sep 17 00:00:00 2001 From: hoswey Date: Sat, 26 Dec 2015 14:17:24 +0800 Subject: [PATCH 1/2] implants the #75 reader writer lock --- pom.xml | 1 + reader-writer-lock/etc/reader-writer-lock.png | Bin 0 -> 37534 bytes .../etc/reader-writer-lock.ucls | 94 +++++++++++ reader-writer-lock/index.md | 29 ++++ reader-writer-lock/pom.xml | 18 +++ .../com/iluwatar/reader/writer/lock/App.java | 82 ++++++++++ .../com/iluwatar/reader/writer/lock/Lock.java | 18 +++ .../reader/writer/lock/ReaderWriterLock.java | 147 ++++++++++++++++++ .../iluwatar/reader/writer/lock/AppTest.java | 20 +++ 9 files changed, 409 insertions(+) create mode 100644 reader-writer-lock/etc/reader-writer-lock.png create mode 100644 reader-writer-lock/etc/reader-writer-lock.ucls create mode 100644 reader-writer-lock/index.md create mode 100644 reader-writer-lock/pom.xml create mode 100644 reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/App.java create mode 100644 reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Lock.java create mode 100644 reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/ReaderWriterLock.java create mode 100644 reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/AppTest.java 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 0000000000000000000000000000000000000000..06207b502bf9387126a63a4f84cde5485903fd7b GIT binary patch literal 37534 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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); + + } +} From 77d45c35e0ae886c01493e162adf6023dbcb4155 Mon Sep 17 00:00:00 2001 From: hoswey Date: Sun, 10 Jan 2016 21:23:52 +0800 Subject: [PATCH 2/2] implements the #75 reader writer lock, fix the problem of review --- reader-writer-lock/etc/reader-writer-lock.png | Bin 37534 -> 39623 bytes .../etc/reader-writer-lock.ucls | 80 ++++--- reader-writer-lock/index.md | 4 +- reader-writer-lock/pom.xml | 38 ++-- .../com/iluwatar/reader/writer/lock/App.java | 83 +++----- .../com/iluwatar/reader/writer/lock/Lock.java | 18 -- .../iluwatar/reader/writer/lock/Reader.java | 40 ++++ .../reader/writer/lock/ReaderWriterLock.java | 200 ++++++++++++------ .../iluwatar/reader/writer/lock/Writer.java | 40 ++++ .../iluwatar/reader/writer/lock/AppTest.java | 4 - .../writer/lock/ReaderAndWriterTest.java | 84 ++++++++ .../reader/writer/lock/ReaderTest.java | 45 ++++ .../reader/writer/lock/WriterTest.java | 52 +++++ 13 files changed, 482 insertions(+), 206 deletions(-) delete mode 100644 reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Lock.java create mode 100644 reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Reader.java create mode 100644 reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Writer.java create mode 100644 reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderAndWriterTest.java create mode 100644 reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderTest.java create mode 100644 reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/WriterTest.java diff --git a/reader-writer-lock/etc/reader-writer-lock.png b/reader-writer-lock/etc/reader-writer-lock.png index 06207b502bf9387126a63a4f84cde5485903fd7b..f7b6005309263d2dc8baf7505fd68091a580011b 100644 GIT binary patch literal 39623 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 literal 37534 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 - + + + + + + + - + - - + - - + - - - - - - - - - + @@ -54,36 +54,28 @@ - - + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - - + + + diff --git a/reader-writer-lock/index.md b/reader-writer-lock/index.md index 75f57a4cd..91d16892c 100644 --- a/reader-writer-lock/index.md +++ b/reader-writer-lock/index.md @@ -1,7 +1,7 @@ --- layout: pattern -title: Producer Consumer -folder: reader writer lock +title: Reader Writer Lock +folder: reader-writer-lock permalink: /patterns/reader-writer-lock/ categories: Concurrent tags: diff --git a/reader-writer-lock/pom.xml b/reader-writer-lock/pom.xml index f1553a4d1..14b17011d 100644 --- a/reader-writer-lock/pom.xml +++ b/reader-writer-lock/pom.xml @@ -1,18 +1,24 @@ - - 4.0.0 - - com.iluwatar - java-design-patterns - 1.9.0-SNAPSHOT - - reader-writer-lock - - - junit - junit - test - - + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.10.0-SNAPSHOT + + reader-writer-lock + + + junit + junit + test + + + org.mockito + mockito-core + 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 index aa36ef47c..1d02b6cad 100644 --- 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 @@ -1,24 +1,31 @@ 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.concurrent.TimeUnit; 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. + * + * In a multiple thread applications, the threads may try to synchronize the shared resources + * regardless of read or write operation. It leads to a low performance especially in a "read more + * write less" system as indeed the read operations are thread-safe to another read operation. *

- * Below example use two mutexes to demonstrate the concurrent access of mutilple readers and + * Reader writer lock is a synchronization primitive that try to resolve this problem. This pattern + * allows concurrent access for read-only operations, while write operations require exclusive + * access. This means that multiple threads can read the data in parallel but an exclusive lock is + * needed for writing or modifying data. When a writer is writing the data, all other writers or + * readers will be blocked until the writer is finished writing. + * + *

+ * This example use two mutex to demonstrate the concurrent access of multiple readers and * writers. * + * + * @author hongshuwei@gmail.com */ public class App { - private static Random ran = new Random(); - /** * Program entry point * @@ -26,57 +33,25 @@ public class App { */ public static void main(String[] args) { - ExecutorService es = Executors.newFixedThreadPool(1000); + ExecutorService executeService = 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() { + // Start 10 readers + IntStream.range(0, 10) + .forEach(i -> executeService.submit(new Reader("Reader " + i, lock.readLock()))); - @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() { + // Start 10 writers + IntStream.range(0, 10) + .forEach(i -> executeService.submit(new Writer("Writer " + i, lock.writeLock()))); + // In the system console, it can see that the read operations are executed concurrently while + // write operations are exclusive. + executeService.shutdown(); try { - Thread.sleep((long) (ran.nextFloat() * 10)); + executeService.awaitTermination(5, TimeUnit.SECONDS); } catch (InterruptedException e) { - e.printStackTrace(); + System.out.println("Error waiting for ExecutorService shutdown"); } + } + } 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 deleted file mode 100644 index 7cbe47749..000000000 --- a/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Lock.java +++ /dev/null @@ -1,18 +0,0 @@ -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/Reader.java b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Reader.java new file mode 100644 index 000000000..5704fdf28 --- /dev/null +++ b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Reader.java @@ -0,0 +1,40 @@ +package com.iluwatar.reader.writer.lock; + +import java.util.concurrent.locks.Lock; + +/** + * Reader class, read when it acquired the read lock + */ +public class Reader implements Runnable { + + private Lock readLock; + + private String name; + + public Reader(String name, Lock readLock) { + this.name = name; + this.readLock = readLock; + } + + @Override + public void run() { + readLock.lock(); + try { + read(); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + readLock.unlock(); + } + } + + /** + * Simulate the read operation + * + */ + public void read() throws InterruptedException { + System.out.println(name + " begin"); + Thread.sleep(100); + System.out.println(name + " finish"); + } +} 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 index 2d92696e3..b7edd149c 100644 --- 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 @@ -2,62 +2,103 @@ package com.iluwatar.reader.writer.lock; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; /** * Class responsible for control the access for reader or writer + * + * Allows multiple readers to hold the lock at same time, but if any writer holds the lock then + * readers wait. If reader holds the lock then writer waits. This lock is not fair. */ -public class ReaderWriterLock { +public class ReaderWriterLock implements ReadWriteLock { + + + private Object readerMutex = new Object(); + + private int currentReaderCount = 0; /** - * Mutex for reader + * Global mutex is used to indicate that whether reader or writer gets the lock in the moment. + *

+ * 1. When it contains the reference of {@link readerLock}, it means that the lock is acquired by + * the reader, another reader can also do the read operation concurrently.
+ * 2. When it contains the reference of reference of {@link writerLock}, it means that the lock is + * acquired by the writer exclusively, no more reader or writer can get the lock. + *

+ * This is the most important field in this class to control the access for reader/writer. */ - 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(); + private Set globalMutex = new HashSet<>(); + private ReadLock readerLock = new ReadLock(); + private WriteLock writerLock = new WriteLock(); + @Override public Lock readLock() { - return readLock; + return readerLock; } - + @Override public Lock writeLock() { - return writeLock; + return writerLock; } + /** + * return true when globalMutex hold the reference of writerLock + */ + private boolean doesWriterOwnThisLock() { + return globalMutex.contains(writerLock); + } + + /** + * return true when globalMutex hold the reference of readerLock + */ + private boolean doesReaderOwnThisLock() { + return globalMutex.contains(readerLock); + } + + /** + * Nobody get the lock when globalMutex contains nothing + * + */ + private boolean isLockFree() { + return globalMutex.isEmpty(); + } + + private void waitUninterruptibly(Object o) { + try { + o.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } /** * Reader Lock, can be access for more than one reader concurrently if no writer get the lock */ - private class ReaderLock implements Lock { + private class ReadLock implements Lock { @Override public void lock() { - synchronized (r) { - - readerCount++; - if (readerCount == 1) { - - synchronized (g) { + synchronized (readerMutex) { + currentReaderCount++; + if (currentReaderCount == 1) { + // Try to get the globalMutex lock for the first reader + synchronized (globalMutex) { while (true) { - if (isLockFree() || isReaderOwnThisLock()) { - g.add(this); + // If the no one get the lock or the lock is locked by reader, just set the reference + // to the globalMutex to indicate that the lock is locked by Reader. + if (isLockFree() || doesReaderOwnThisLock()) { + globalMutex.add(this); break; } else { - waitUninterruptely(g); + // If lock is acquired by the write, let the thread wait until the writer release + // the lock + waitUninterruptibly(globalMutex); } } } @@ -66,82 +107,105 @@ public class ReaderWriterLock { } } - @Override public void unlock() { - synchronized (r) { - readerCount--; - if (readerCount == 0) { - synchronized (g) { - g.remove(this); - g.notifyAll(); + synchronized (readerMutex) { + currentReaderCount--; + // Release the lock only when it is the last reader, it is ensure that the lock is released + // when all reader is completely. + if (currentReaderCount == 0) { + synchronized (globalMutex) { + // Notify the waiter, mostly the writer + globalMutex.remove(this); + globalMutex.notifyAll(); } } } } - } + @Override + public void lockInterruptibly() throws InterruptedException { + throw new UnsupportedOperationException(); + } + @Override + public boolean tryLock() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { + throw new UnsupportedOperationException(); + } + + @Override + public Condition newCondition() { + throw new UnsupportedOperationException(); + } + + } /** * Writer Lock, can only be accessed by one writer concurrently */ - private class WriterLock implements Lock { + private class WriteLock implements Lock { @Override public void lock() { - synchronized (g) { + synchronized (globalMutex) { while (true) { - + // When there is no one acquired the lock, just put the writeLock reference to the + // globalMutex to indicate that the lock is acquired by one writer. + // It is ensure that writer can only get the lock when no reader/writer acquired the lock. if (isLockFree()) { - g.add(this); + globalMutex.add(this); break; - } else if (isWriterOwnThisLock()) { - waitUninterruptely(g); - } else if (isReaderOwnThisLock()) { - waitUninterruptely(g); + } else if (doesWriterOwnThisLock()) { + // Wait when other writer get the lock + waitUninterruptibly(globalMutex); + } else if (doesReaderOwnThisLock()) { + // Wait when other reader get the lock + waitUninterruptibly(globalMutex); } else { - throw new RuntimeException("it should never reach here"); + throw new AssertionError("it should never reach here"); } } } } - @Override public void unlock() { - synchronized (g) { - g.remove(this); - g.notifyAll(); + synchronized (globalMutex) { + globalMutex.remove(this); + // Notify the waiter, other writer or reader + globalMutex.notifyAll(); } } - } - private boolean isWriterOwnThisLock() { - return g.contains(writeLock); - } + @Override + public void lockInterruptibly() throws InterruptedException { + throw new UnsupportedOperationException(); + } - private boolean isReaderOwnThisLock() { - return g.contains(readLock); - } + @Override + public boolean tryLock() { + throw new UnsupportedOperationException(); + } - private boolean isLockFree() { - return g.isEmpty(); - } + @Override + public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { + throw new UnsupportedOperationException(); + } - - - private void waitUninterruptely(Object o) { - try { - o.wait(); - } catch (InterruptedException e) { - e.printStackTrace(); + @Override + public Condition newCondition() { + throw new UnsupportedOperationException(); } } -} +} diff --git a/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Writer.java b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Writer.java new file mode 100644 index 000000000..d7afa385a --- /dev/null +++ b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Writer.java @@ -0,0 +1,40 @@ +package com.iluwatar.reader.writer.lock; + +import java.util.concurrent.locks.Lock; + +/** + * Writer class, write when it acquired the write lock + */ +public class Writer implements Runnable { + + private Lock writeLock = null; + + private String name; + + public Writer(String name, Lock writeLock) { + this.name = name; + this.writeLock = writeLock; + } + + + @Override + public void run() { + writeLock.lock(); + try { + write(); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + writeLock.unlock(); + } + } + + /** + * Simulate the write operation + */ + public void write() throws InterruptedException { + System.out.println(name + " begin"); + Thread.sleep(100); + System.out.println(name + " finish"); + } +} 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 index 95656b14e..5dd6feaab 100644 --- 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 @@ -2,12 +2,8 @@ package com.iluwatar.reader.writer.lock; import org.junit.Test; -import com.iluwatar.reader.writer.lock.App; - /** - * * Application test - * */ public class AppTest { diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderAndWriterTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderAndWriterTest.java new file mode 100644 index 000000000..b9516f3a7 --- /dev/null +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderAndWriterTest.java @@ -0,0 +1,84 @@ +package com.iluwatar.reader.writer.lock; + +import static org.mockito.Mockito.after; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * @author hongshuwei@gmail.com + */ +public class ReaderAndWriterTest { + + ExecutorService executeService; + + @Before + public void setup() { + executeService = Executors.newFixedThreadPool(2); + } + + /** + * Verify reader and writer can only get the lock to read and write orderly + */ + @Test + public void testReadAndWrite() throws Exception { + + ReaderWriterLock lock = new ReaderWriterLock(); + + Reader reader1 = spy(new Reader("Reader 1", lock.readLock())); + Writer writer1 = spy(new Writer("Writer 1", lock.writeLock())); + + executeService.submit(reader1); + // Let reader1 execute first + Thread.sleep(50); + executeService.submit(writer1); + + verify(reader1, timeout(99).atLeastOnce()).read(); + verify(writer1, after(10).never()).write(); + verify(writer1, timeout(100).atLeastOnce()).write(); + + } + + /** + * Verify reader and writer can only get the lock to read and write orderly + */ + @Test + public void testWriteAndRead() throws Exception { + + ExecutorService executeService = Executors.newFixedThreadPool(2); + ReaderWriterLock lock = new ReaderWriterLock(); + + Reader reader1 = spy(new Reader("Reader 1", lock.readLock())); + Writer writer1 = spy(new Writer("Writer 1", lock.writeLock())); + + executeService.submit(writer1); + // Let reader1 execute first + Thread.sleep(50); + executeService.submit(reader1); + + verify(writer1, timeout(99).atLeastOnce()).write(); + verify(reader1, after(10).never()).read(); + verify(reader1, timeout(100).atLeastOnce()).read(); + + + } + + @After + public void tearDown() { + executeService.shutdown(); + try { + executeService.awaitTermination(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + System.out.println("Error waiting for ExecutorService shutdown"); + } + } +} + diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderTest.java new file mode 100644 index 000000000..c18bc1b2b --- /dev/null +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderTest.java @@ -0,0 +1,45 @@ +package com.iluwatar.reader.writer.lock; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; + +/** + * @author hongshuwei@gmail.com + */ +public class ReaderTest { + + /** + * Verify that multiple readers can get the read lock concurrently + */ + @Test + public void testRead() throws Exception { + + ExecutorService executeService = Executors.newFixedThreadPool(2); + ReaderWriterLock lock = new ReaderWriterLock(); + + Reader reader1 = spy(new Reader("Reader 1", lock.readLock())); + Reader reader2 = spy(new Reader("Reader 2", lock.readLock())); + + executeService.submit(reader1); + executeService.submit(reader2); + + // Read operation will hold the read lock 100 milliseconds, so here we guarantee that each + // readers can read in 99 milliseconds to prove that multiple read can perform in the same time. + verify(reader1, timeout(99).atLeastOnce()).read(); + verify(reader2, timeout(99).atLeastOnce()).read(); + + executeService.shutdown(); + try { + executeService.awaitTermination(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + System.out.println("Error waiting for ExecutorService shutdown"); + } + } +} diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/WriterTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/WriterTest.java new file mode 100644 index 000000000..c81a56dc1 --- /dev/null +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/WriterTest.java @@ -0,0 +1,52 @@ +package com.iluwatar.reader.writer.lock; + +import static org.mockito.Mockito.after; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; + +/** + * @author hongshuwei@gmail.com + */ +public class WriterTest { + + /** + * Verify that multiple writers will get the lock in order. + */ + @Test + public void testWrite() throws Exception { + + ExecutorService executeService = Executors.newFixedThreadPool(2); + ReaderWriterLock lock = new ReaderWriterLock(); + + Writer writer1 = spy(new Writer("Writer 1", lock.writeLock())); + Writer writer2 = spy(new Writer("Writer 2", lock.writeLock())); + + executeService.submit(writer1); + // Let write1 execute first + Thread.sleep(50); + executeService.submit(writer2); + + // Write operation will hold the write lock 100 milliseconds, so here we verify that when two + // write excute concurrently + // 1. The first write will get the lock and and write in 60ms + // 2. The second writer will cannot get the lock when first writer get the lock + // 3. The second writer will get the lock as last + verify(writer1, timeout(10).atLeastOnce()).write(); + verify(writer2, after(10).never()).write(); + verify(writer2, timeout(100).atLeastOnce()).write(); + + executeService.shutdown(); + try { + executeService.awaitTermination(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + System.out.println("Error waiting for ExecutorService shutdown"); + } + } +}