美文网首页
使用ReentrantLock实现读写锁

使用ReentrantLock实现读写锁

作者: 周群力 | 来源:发表于2019-09-25 18:36 被阅读0次

    问题:实现一个读写锁

    :方法很多,可以用信号量,本文介绍用Monitor模型(即JDK提供的ReentrantLock)来实现读写锁。其他方案可以参考C语言实现方案

    直觉上的理解
    可以把执行中的读操作想象成一个一个水池里的水,如图中R部分代表水池中的水,即执行中的读操作(read)。获取读锁相当于往水池灌1平米的水,释放读锁相当于从水池放出1平米的水。

    image.png

    当加上写锁的时候,相当于在水池中插入一个防洪大坝。后续如果再有获取读锁的操作(往水池灌水的操作),需要判断水池里有没有大坝,有大坝则不能继续灌水


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

    相关文章

      网友评论

          本文标题:使用ReentrantLock实现读写锁

          本文链接:https://www.haomeiwen.com/subject/lukwuctx.html