Lock(ReentrantReadWriteLock)内部读写锁的问题
ReentrantReadWriteLock维护着一对锁,一个读锁和一个写锁。通过分离读锁和写锁,使得并发性比一般的排他锁有了较大的提升:在同一时间可以允许多个读线程同时访问,但是在写线程访问时,所有读线程和写线程都会被阻塞。
关于共享变量
在ReentrantLock中使用一个int类型的state来表示同步状态,该值表示锁被一个线程重复获取的次数。但是读写锁ReentrantReadWriteLock内部维护着两个一对锁,需要用一个变量维护多种状态。所以读写锁采用“按位切割使用”的方式来维护这个变量,将其切分为两部分,高16为表示读,低16为表示写。分割之后,读写锁是如何迅速确定读锁和写锁的状态呢?通过为运算。假如当前同步状态为S,那么写状态等于 S & 0x0000FFFF(将高16位全部抹去),读状态等于S >>> 16(无符号补0右移16位)。
写锁的获取
和reentrantlock锁获取的状态基本相同,主要多了一层有没有读锁的存在
protected final boolean tryAcquire(int acquires) { Thread current = Thread.currentThread(); //当前锁个数 int c = getState(); //写锁 int w = exclusiveCount(c); if (c != 0) {//有没有读锁的存在 //c != 0 && w == 0 表示存在读锁 //当前线程不是已经获取写锁的线程 if (w == 0 || current != getExclusiveOwnerThread()) return false; //超出最大范围 if (w + exclusiveCount(acquires) > MAX_COUNT) throw new Error("Maximum lock count exceeded"); setState(c + acquires); return true; } //是否需要阻塞 if (writerShouldBlock() || !compareAndSetState(c, c + acquires)) return false; //设置获取锁的线程为当前线程 setExclusiveOwnerThread(current); return true; }
读锁的获取,麻烦很多
读锁的获取只要没有写锁占用并且不是当前线程(可能存在写锁降级的情况所以还需要后面一个判断,降级了我自己是可以去读的啊),而且读锁没有超过最大获取数量都可以尝试获取读锁。因为读锁是有有多个的,每个读锁也是可重入的,也就是大于0,我们需要专门设置一个变量去记录
protected final int tryAcquireShared(int unused) { Thread current = Thread.currentThread(); int c = getState(); if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current) //写锁不等于0的情况下,验证是否是当前写锁尝试获取读锁 return -1; int r = sharedCount(c); //获取读锁数量 if (!readerShouldBlock() && //读锁不需要阻塞 r < MAX_COUNT && //读锁小于最大读锁数量 compareAndSetState(c, c + SHARED_UNIT)) { //CAS操作尝试设置获取读锁 也就是高位加1 if (r == 0) { //当前线程第一个并且第一次获取读锁, firstReader = current; firstReaderHoldCount = 1; } else if (firstReader == current) { //当前线程是第一次获取读锁的线程 firstReaderHoldCount++; } else { // 当前线程不是第一个获取读锁的线程,放入线程本地变量 HoldCounter rh = cachedHoldCounter; if (rh == null || rh.tid != getThreadId(current)) cachedHoldCounter = rh = readHolds.get(); else if (rh.count == 0) readHolds.set(rh); rh.count++; } return 1; } return fullTryAcquireShared(current); }
尝试用ReentrantReadWriteLock实现一个简单的缓存?
自己实现一个读写锁?
读写锁会发生写饥饿的情况吗?如果发生,有没有比较好的解决办法?
ReentrantReadWriteLock会发生饥饿的情况,具体JDK8中新增的改进读写锁---StampedLock改进
具体参考:(下面两个讲的非常好)
本文来自博客园,作者:LeeJuly,转载请注明原文链接:https://www.cnblogs.com/peterleee/p/10902438.html

浙公网安备 33010602011771号