#354 Add maven model for feature toggle design pattern
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
package com.iluwatar.reader.writer.lock;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
*
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* This example use two mutex to demonstrate the concurrent access of multiple readers and writers.
|
||||
*
|
||||
*
|
||||
* @author hongshuwei@gmail.com
|
||||
*/
|
||||
public class App {
|
||||
|
||||
/**
|
||||
* Program entry point
|
||||
*
|
||||
* @param args command line args
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
|
||||
ExecutorService executeService = Executors.newFixedThreadPool(10);
|
||||
ReaderWriterLock lock = new ReaderWriterLock();
|
||||
|
||||
// Start 5 readers
|
||||
IntStream.range(0, 5)
|
||||
.forEach(i -> executeService.submit(new Reader("Reader " + i, lock.readLock())));
|
||||
|
||||
// Start 5 writers
|
||||
IntStream.range(0, 5)
|
||||
.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 {
|
||||
executeService.awaitTermination(5, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
System.out.println("Error waiting for ExecutorService shutdown");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -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(250);
|
||||
System.out.println(name + " finish");
|
||||
}
|
||||
}
|
@@ -0,0 +1,211 @@
|
||||
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 implements ReadWriteLock {
|
||||
|
||||
|
||||
private Object readerMutex = new Object();
|
||||
|
||||
private int currentReaderCount = 0;
|
||||
|
||||
/**
|
||||
* Global mutex is used to indicate that whether reader or writer gets the lock in the moment.
|
||||
* <p>
|
||||
* 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. <br>
|
||||
* 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.
|
||||
* <p>
|
||||
* This is the most important field in this class to control the access for reader/writer.
|
||||
*/
|
||||
private Set<Object> globalMutex = new HashSet<>();
|
||||
|
||||
private ReadLock readerLock = new ReadLock();
|
||||
private WriteLock writerLock = new WriteLock();
|
||||
|
||||
@Override
|
||||
public Lock readLock() {
|
||||
return readerLock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Lock 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 ReadLock implements Lock {
|
||||
|
||||
@Override
|
||||
public void lock() {
|
||||
|
||||
synchronized (readerMutex) {
|
||||
|
||||
currentReaderCount++;
|
||||
if (currentReaderCount == 1) {
|
||||
// Try to get the globalMutex lock for the first reader
|
||||
synchronized (globalMutex) {
|
||||
while (true) {
|
||||
// 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 {
|
||||
// If lock is acquired by the write, let the thread wait until the writer release
|
||||
// the lock
|
||||
waitUninterruptibly(globalMutex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlock() {
|
||||
|
||||
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 WriteLock implements Lock {
|
||||
|
||||
@Override
|
||||
public void lock() {
|
||||
|
||||
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()) {
|
||||
globalMutex.add(this);
|
||||
break;
|
||||
} 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 AssertionError("it should never reach here");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlock() {
|
||||
|
||||
synchronized (globalMutex) {
|
||||
globalMutex.remove(this);
|
||||
// Notify the waiter, other writer or reader
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -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(250);
|
||||
System.out.println(name + " finish");
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user