问题:实现一个读写锁
解:方法很多,可以用信号量,本文介绍用Monitor模型(即JDK提供的ReentrantLock)来实现读写锁。其他方案可以参考C语言实现方案
直觉上的理解:
可以把执行中的读操作想象成一个一个水池里的水,如图中R部分代表水池中的水,即执行中的读操作(read)。获取读锁相当于往水池灌1平米的水,释放读锁相当于从水池放出1平米的水。
当加上写锁的时候,相当于在水池中插入一个防洪大坝。后续如果再有获取读锁的操作(往水池灌水的操作),需要判断水池里有没有大坝,有大坝则不能继续灌水
image.png
但是这里会有一个corner case:假如获取读锁的操作的代码是:
while(有大坝){
await
}
do灌水
有可能执行完第3行、准备执行第4行的时候,另一个线程插入了大坝(获取写锁),导致执行第四行时,明明有大坝还灌水。大概就是图上这种感觉:
image.png
怎么解决这个问题呢?方案是:获取写锁时,先插入大坝,然后等待池子里的水泄洪,当水流干了,才算真正的获取到写锁。如图
image.png
实现
接口:
public interface Lock {
void lock() throws InterruptedException;
void unlock();
}
实现类:
public class ReadWriteLock {
private final Condition noWrite;
private final AtomicInteger readCounter;
private final AtomicInteger writeCounter;
private final Condition readExit;
private final ReentrantLock rawLock;
public ReadWriteLock() {
ReentrantLock lock = new ReentrantLock();
this.rawLock=lock;
AtomicInteger readCounter = new AtomicInteger(0);
this.readCounter = readCounter;
AtomicInteger writeCounter = new AtomicInteger(0);
this.writeCounter = writeCounter;
Condition noWrite = lock.newCondition();
this.noWrite = noWrite;
Condition readExit = lock.newCondition();
this.readExit = readExit;
}
Lock readLock() {
return new ReadLock(noWrite, readCounter, writeCounter, readExit,rawLock);
}
Lock writeLock() {
return new WriteLock(noWrite, readCounter, writeCounter, readExit,rawLock);
}
static class ReadLock implements Lock {
private Condition noWrite;
private AtomicInteger readCounter;
private AtomicInteger writeCounter;
private Condition readExit;
private ReentrantLock rawLock;
public ReadLock(Condition noWrite, AtomicInteger readCounter, AtomicInteger writeCounter,
Condition readExit, ReentrantLock rawLock) {
this.noWrite = noWrite;
this.readCounter = readCounter;
this.writeCounter = writeCounter;
this.readExit = readExit;
this.rawLock = rawLock;
}
@Override
public void lock() throws InterruptedException {
//如果有防洪大坝了就不要再灌水了
while (writeCounter.get() > 0) {
noWrite.await();
}
//进水
readCounter.incrementAndGet();
}
@Override
public void unlock() {
//出水
readCounter.decrementAndGet();
rawLock.lock();
readExit.signalAll();
rawLock.unlock();
}
}
static class WriteLock implements Lock {
private Condition noWrite;
private AtomicInteger readCounter;
private AtomicInteger writeCounter;
private Condition readExit;
private ReentrantLock rawLock;
public WriteLock(Condition noWrite, AtomicInteger readCounter, AtomicInteger writeCounter, Condition readExit,
ReentrantLock rawLock) {
this.noWrite = noWrite;
this.readCounter = readCounter;
this.writeCounter = writeCounter;
this.readExit = readExit;
this.rawLock = rawLock;
}
@Override
public void lock() throws InterruptedException {
rawLock.lock();
//插入防洪大坝
while(writeCounter.get()>0){
noWrite.await();
}
writeCounter.incrementAndGet();
//等待泄洪
while (readCounter.get()>0){
readExit.await();
}
//泄洪完成,此时不再有并发读和并发写
rawLock.unlock();
}
@Override
public void unlock() {
//拔出防洪大坝
writeCounter.decrementAndGet();
rawLock.lock();
//通知
noWrite.signalAll();
rawLock.unlock();
}
}
}
测试
public class ReadWriteLockTest {
@Test
public void multiRead() {
ReadWriteLock lock = new ReadWriteLock();
Lock rl = lock.readLock();
new Thread(() -> {
try {
rl.lock();
System.out.println("t1 prepare to sleep");
Thread.sleep(2000);
System.out.println("t1 wake up");
rl.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
rl.lock();
System.out.println("t2 prepare to sleep");
Thread.sleep(2000);
System.out.println("t2 wake up");
rl.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
@Test
public void noWriteWhenReadLock() throws InterruptedException {
ReadWriteLock lock = new ReadWriteLock();
Lock rl = lock.readLock();
new Thread(() -> {
try {
rl.lock();
System.out.println("t1 prepare to sleep");
Thread.sleep(2000);
System.out.println("t1 wake up");
rl.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
rl.lock();
System.out.println("t2 prepare to sleep");
Thread.sleep(2000);
System.out.println("t2 wake up");
rl.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
//写锁
Lock wl = lock.writeLock();
new Thread(() -> {
try {
wl.lock();
System.out.println("t3(write) prepare to sleep");
Thread.sleep(2000);
System.out.println("t3(write) wake up");
wl.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
Thread.sleep(6000);
}
}
网友评论