implants the #75 reader writer lock

This commit is contained in:
hoswey 2015-12-26 14:17:24 +08:00
parent a0151826ad
commit 3731d26f6d
9 changed files with 409 additions and 0 deletions

View File

@ -60,6 +60,7 @@
<module>intercepting-filter</module>
<module>producer-consumer</module>
<module>poison-pill</module>
<module>reader-writer-lock</module>
<module>lazy-loading</module>
<module>service-layer</module>
<module>specification</module>

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.1.8" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
realizations="true" associations="true" dependencies="false" nesting-relationships="true">
<class id="1" language="java" name="com.iluwatar.reader.writer.lock.ReaderWriterLock.ReaderLock"
project="reader-writer-lock"
file="/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/ReaderWriterLock.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="305" y="511"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="2" language="java" name="com.iluwatar.reader.writer.lock.ReaderWriterLock" project="reader-writer-lock"
file="/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/ReaderWriterLock.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="394" y="270"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="3" language="java" name="com.iluwatar.reader.writer.lock.App" project="reader-writer-lock"
file="/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/App.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="671" y="274"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<interface id="4" language="java" name="com.iluwatar.reader.writer.lock.Lock" project="reader-writer-lock"
file="/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Lock.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="101" width="148" x="322" y="597"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</interface>
<class id="5" language="java" name="com.iluwatar.reader.writer.lock.ReaderWriterLock.WriterLock"
project="reader-writer-lock"
file="/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/ReaderWriterLock.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="488" y="509"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<nesting id="6">
<end type="SOURCE" refId="2"/>
<end type="TARGET" refId="5"/>
</nesting>
<realization id="7">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="4"/>
</realization>
<realization id="8">
<end type="SOURCE" refId="1"/>
<end type="TARGET" refId="4"/>
</realization>
<association id="9">
<end type="SOURCE" refId="2" navigable="false">
<attribute id="10" name="writeLock"/>
<multiplicity id="11" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="5" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<association id="12">
<end type="SOURCE" refId="2" navigable="false">
<attribute id="13" name="readLock"/>
<multiplicity id="14" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="1" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<nesting id="15">
<end type="SOURCE" refId="2"/>
<end type="TARGET" refId="1"/>
</nesting>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</classifier-display>
<association-display labels="true" multiplicity="true"/>
</class-diagram>

View File

@ -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**
* [Readerswriter lock](https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock)
* [Readerswriters_problem](https://en.wikipedia.org/wiki/Readers%E2%80%93writers_problem)

View File

@ -0,0 +1,18 @@
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.9.0-SNAPSHOT</version>
</parent>
<artifactId>reader-writer-lock</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -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 readerswriters
* problems. An RW lock allows concurrent access for read-only operations, while write operations
* require exclusive access.
* <p>
* 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();
}
}
}

View File

@ -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();
}

View File

@ -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<Object> 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();
}
}
}

View File

@ -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);
}
}