java.util.concurrent学习(十) ReadWriteLock,ReentrantReadWriteLock

Lock框架

ReadWriteLock

ReadWriteLock是读写锁的顶端接口,定义了获取读锁和写锁的方法。

ReentrantReadWriteLock

锁的实现基础Sync

ReentrantReadWriteLock是ReadWriteLock的默认实现类,其内部定义了一个类似于ReentrantLock中的Sync的抽象类,不同的是,ReentrantReadWriteLock中的Sync需要同时实现共享锁和排他锁,了解AbstractQueuedSynchronizer的同学都知道,AbstractQueuedSynchronizer提供了共享锁和排他锁的操作,Sync就是通过继承AbstractQueuedSynchronizer来实现的。

锁计数器:

Sync定义了两个内部类HoldCounter(锁计数器)和ThreadLocalHoldCounter(线程私有的锁计数器),而锁计数器内部只维护了一个int值来计数,那么想同时记录共享锁和排他锁的数量就需要用这个int值的高16位和低16位来记了。既然用高低位来记录,那也同时说明锁的数量是有限的,且最大值是65535。

  //锁计数器
  static final class HoldCounter {
            //记录锁的值
            int count = 0;
            //只想线程id,避免垃圾回收
            final long tid = getThreadId(Thread.currentThread());
        }

        
   //线程私有的计数器
  static final class ThreadLocalHoldCounter
            extends ThreadLocal<HoldCounter> {
           //初始化一个锁计数器
            public HoldCounter initialValue() {
                return new HoldCounter();
            }
        }

//共享锁计数器坐标
 static final int SHARED_SHIFT = 16;
//共享锁计数器增加单位 高16位
 static final int SHARED_UNIT = (1 << SHARED_SHIFT);
//锁最大数,共享锁和独享锁都是16位二级制计数,因此最大数一样 都是6553
 static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
//排他锁的标记,通过将锁的count值与该值进行与运算,获取低16位的排他锁数量
 static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
//将锁的count值无符号左移16位,获取共享锁数量
 static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
//将锁的count值与该值进行与运算,获取低16位的排他锁数量
 static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }

Sync API

方法 描述
tryAcquire(int acquires) 尝试获取排他锁,增加排他锁数量并记录锁状态,返回true/false。
tryRelease(int releases) 尝试释放排他锁,减少排他锁数量并记录锁状态,返回true/false。
tryAcquireShared(int unused) 尝试获取共享锁,增加共享锁数量并记录锁状态,返回1(成功)/-1(失败)。
tryReleaseShared(int unused 尝试释放共享锁,较少共享锁数量并记录锁状态,返回true/false。
tryWriteLock() 尝试获取写锁,即获取排他锁
tryReadLock() 尝试获取读锁,即获取共享锁
newCondition() 绑定一个condition,可以操作进入condition队列
getOwner() 获取排他锁持有线程
getReadLockCount() 获取读锁数量,即共享锁数量
getWriteHoldCount() 获取写锁数量,即排他锁数量
isWriteLocked() 是否被写锁持有

非公平锁和公平锁

ReentrantReadWriteLock有两个内部类NonfairSync(非公平锁)和FairSync(公平锁),当一个ReentrantReadWriteLock在获取锁时,会通过这两个锁的writerShouldBlock()和readerShouldBlock()来判断是否需要阻塞,我们来看一下这两个类的方法实现。

 static final class FairSync extends Sync {
        private static final long serialVersionUID = -2274990926593161451L;
        final boolean writerShouldBlock() {
           //队列中是否还有前驱结点
            return hasQueuedPredecessors();
        }
        final boolean readerShouldBlock() {
           //队列中是否还有前驱节点
            return hasQueuedPredecessors();
        }
    }


    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = -8159625535654395037L;
        //非公平所在写锁时永远不阻塞
        final boolean writerShouldBlock() {
            return false; 
        }
        final boolean readerShouldBlock() {
            //队列中在等待获取锁的第一个节点是否是排他锁
            return apparentlyFirstQueuedIsExclusive();
        }
    }

可以看到,公平锁在判断是否需要阻塞式取决于队列中是否还有等待时间更长的线程,非公平所在获取写锁时,永远不用阻塞,在获取读锁时,取决于队列中下一个需要获取的节点是否是排他锁,如果是的话则阻塞。

读锁和写锁

ReentrantReadWriteLock有两个内部类ReadLock(读锁)和WriteLock(写锁),分别操作共享锁和独享锁。两个类都实现了Lock,两个类内部都维护了一个Sync私有变量,在实例化ReentrantReadWriteLock时,会初始化ReadLock和WriteLock给ReentrantReadWriteLock的私有变量readerLock和writerLock,想要使用这两个锁,必须先实例化ReentrantReadWriteLock,通过ReentrantReadWriteLock的writeLock()和readLock()来获取实例的内部变量。

ReadLock和WriteLock通过内部变量sync来实现锁的机制,ReadLoad操作共享锁,WriteLock操作独享锁。独享锁可降级位共享锁,共享锁不可升级为独享锁。

 

 

猜你喜欢

转载自blog.csdn.net/top_explore/article/details/91347699