ReentrantReadWriteLock

基本使用

class RWDictionary {
    private final Map<String, Data> m = new TreeMap<String, Data>();
    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private final Lock r = rwl.readLock();
    private final Lock w = rwl.writeLock();
 
    public Data get(String key) {
      r.lock();
      try { return m.get(key); }
      finally { r.unlock(); }
    }
    public String[] allKeys() {
      r.lock();
      try { return m.keySet().toArray(); }
      finally { r.unlock(); }
    }
    public Data put(String key, Data value) {
      w.lock();
      try { return m.put(key, value); }
      finally { w.unlock(); }
    }
    public void clear() {
      w.lock();
      try { m.clear(); }
      finally { w.unlock(); }
    }
  }

成员变量和构造方法

	/**
	* 读锁
	*/
    private final ReentrantReadWriteLock.ReadLock readerLock;
 
 	/**
	* 写锁
	*/
    private final ReentrantReadWriteLock.WriteLock writerLock;
 
 	/**
	* 同步器
	*/
    final Sync sync;

   /**
	* 无参构造,默认初始化非公平锁
	*/
    public ReentrantReadWriteLock() {
        this(false);
    }
	
	/**
	* 根据fair判断初始化的锁是公平锁或非公平锁
	* (公平锁和非公平锁对于读写锁有什么作用?)
	*/
    public ReentrantReadWriteLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
        readerLock = new ReadLock(this); //初始化读锁
        writerLock = new WriteLock(this); //初始化写锁
    }

    public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
    public ReentrantReadWriteLock.ReadLock  readLock()  { return readerLock; }

内部类

Sync

abstract static class Sync extends AbstractQueuedSynchronizer {
       /**
       * 将state分为高16位和低16位两个无符号的值
       * 高16位进行读锁的计数,用其中的低16位表示写锁的状态和重入次数
       * SHARED_SHIFT:分隔的位数
       * SHARED_UNIT:1左移SHARED_SHIFT位,读锁+1时,因为读锁用的是高16位所以读锁+1即加SHARED_UNIT(0000000000000001 0000000000000000)
       * MAX_COUNT:最大的读数量,将1左移SHARED_SHIFT位减1,即0000000000000001 0000000000000000减1
       * EXCLUSIVE_MASK:用于获取写锁的重入次数,写锁用低16位表示,EXCLUSIVE_MASK(0000000000000000 1111111111111111)& int c,假如c = 1
       * 即&0000000000000001 得出结果1
       * (下有详解)
       */
        static final int SHARED_SHIFT   = 16;
        static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
        static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
        static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;

        //获取读锁的数量
        static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
        //获取写锁的重入次数
        static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }

		//用ThreadLocal记录读锁重入的次数
        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();
            }
        }
        //此处不明白的话请阅读下面详解
		//当前线程持有的可重入锁的数量
        private transient ThreadLocalHoldCounter readHolds;

      	//成功获取 readLock 的最后一个线程的保持计数
        private transient HoldCounter cachedHoldCounter;

        /**
        * firstReader 是第一个获得读锁的线程。
        *  firstReaderHoldCount 是 firstReader 的保留计数
        * 只有一个线程获取到了锁,则用firstReaderHoldCount计数
        */
        private transient Thread firstReader = null;
        private transient int firstReaderHoldCount;
		
		/**
		*构造方法
		*/
        Sync() {
            readHolds = new ThreadLocalHoldCounter();
            setState(getState()); // ensures visibility of readHolds
        }

		/**
		* 尝试读锁上锁
		*/
		protected final int tryAcquireShared(int unused) {
            //获取当前需要上锁的线程
            Thread current = Thread.currentThread();
            //获取状态值
            int c = getState();
            /**
            * 1.判断是否有写锁
            * 2.如果有写锁,判断当前线程是否是拥有独占访问权限的线程
            * 3.如果不是返回-1,AQS会调用后续方法将当前线程加入到等待队列中
            */
            if (exclusiveCount(c) != 0 && 
                getExclusiveOwnerThread() != current) 
                return -1;
            //没有写线程或有写线程但当前线程是拥有独占访问权限的线程(当前执行的线程是读线程)
            int r = sharedCount(c); //获取当前读锁数量
            /**
            *	1.判断当前读线程是否应该被上锁执行
            *	2.如果应该被上锁执行,判断当前读线程数是否小于最大线程数
            *	3.如果当前线程应该被上锁执行,并且当前线程数小于最大线程数,则比较并替换读锁的状态值
            */
            if (!readerShouldBlock() && //readerShouldBlock()判断当前读线程是否应该被上锁执行,该方法在公平锁模式下会判断等待队列里是否有线程,非公平锁模式下判断等待队列里是否有写线程
                r < MAX_COUNT && //线程数是否小于最大线程数
                compareAndSetState(c, c + SHARED_UNIT)) { //更新读锁状态值
                if (r == 0) { //如果当前线程数为0
                    firstReader = current; //将当前线程赋值给firstReader 记录第一个获取读锁的线程
                    firstReaderHoldCount = 1; //第一个获取读锁的线程记录重入次数
                } else if (firstReader == current) { //如果当前读线程数不为0且当前线程是第一读锁线程
                    firstReaderHoldCount++; //重入次数++
                } else { //如果当前读线程数不为0且不是第一个读线程
                    HoldCounter rh = cachedHoldCounter; //获取当前线程缓存的计数对象
                    //判断rh是否为null,或当前线程与缓存中的线程是否是同一个
                	//若rh为空或线程不对应,则重新从线程容器中取
                    if (rh == null || rh.tid != getThreadId(current)) //最后一个读线程不存在,或者最后一个读线程存在但是当前线程的id和记录的最后一个读线程不一致
                        cachedHoldCounter = rh = readHolds.get(); //重新从线程容器中取
                    else if (rh.count == 0) //最后一个读线程存在并且当前线程的id和记录的最后一个读线程一致,但是里面的计数为0
                        readHolds.set(rh); //更新HoldCounter对象
                    rh.count++;  //重入数量++
                }
                return 1; //返回获取锁成功
            }
            return fullTryAcquireShared(current); //以上是优化,fullTryAcquireShared()该方法执行的是读锁上锁的完整版
        }

		/**
		*	读锁上锁完整版
		*/
		final int fullTryAcquireShared(Thread current) {
            
            HoldCounter rh = null;
            for (;;) {
                int c = getState(); //获取当前锁的状态值
                /**
                *	判断是否有写线程,或者当前线程可不可以被上锁,
                *	1.如果有写线程,但是当前线程没有独占访问权限,则加入等待队列,如果有独占访问权限,则表明当前线程要降锁级,即原来获取的是写入锁,现在重入后要执行的是读操作,要获取读锁
                *	2.没有写线程,等待队列里面有线程等待,
                *		第一个读线程不是当前线程,计数线程计数值为0,移除掉计数线程,并进入等待队列
                */
                if (exclusiveCount(c) != 0) { //有写锁
                    if (getExclusiveOwnerThread() != current)//独占访问权限的线程不是当前线程
                        return -1; //由AQS加入等待队列
                    
                } else if (readerShouldBlock()) { //有等待的写锁
                    
                    if (firstReader == current) { //第一个读线程是当前线程
                        
                    } else { //第一个读线程不是当前线程
                        if (rh == null) { //计数线程为空
                            rh = cachedHoldCounter; //获取当前线程缓存的计数对象
                            if (rh == null || rh.tid != getThreadId(current)) { //最后一个计数线程为空或者最后一个计数线程的id不是当前线程的计数线程
                                rh = readHolds.get(); //获取当前线程的计数线程
                                if (rh.count == 0) //如果重入次数为0
                                    readHolds.remove(); //移除掉计数线程
                            }
                        }
                        if (rh.count == 0) //重入次数为0,将当前线程加入等待队列中
                            return -1;
                    }
                }
                
                if (sharedCount(c) == MAX_COUNT) //当前读线程数量为最大读线程数量
                    throw new Error("Maximum lock count exceeded");
                if (compareAndSetState(c, c + SHARED_UNIT)) { //设置读线程数量
                    if (sharedCount(c) == 0) { //当前读线程数为0
                        firstReader = current; //当前线程记录为第一个读线程
                        firstReaderHoldCount = 1; //重入数 = 1
                    } else if (firstReader == current) { //当前线程是第一个读线程
                        firstReaderHoldCount++; //重入数++
                    } else {
                        if (rh == null)  
                            rh = cachedHoldCounter;//获取当前线程缓存的计数对象
                        if (rh == null || rh.tid != getThreadId(current)) //当前线程不是最后一个读线程
                            rh = readHolds.get(); //获取当前线程的计数线程
                        else if (rh.count == 0) //计数为0,设置进readHolds
                            readHolds.set(rh);
                        rh.count++; //添加重入次数
                        cachedHoldCounter = rh; // 记录最后的读线程
                    }
                    return 1;
                }
            }
        }

		/**
		*	读锁解锁
		*/
		protected final boolean tryReleaseShared(int unused) {
			//获取当前线程
            Thread current = Thread.currentThread();
            //开始处理锁重入的数据
            //当前线程是否为第一个读线程
            if (firstReader == current) {
                if (firstReaderHoldCount == 1) //如果第一个读锁的重入次数为1
                    firstReader = null; //第一个读线程记录为null
                else
                    firstReaderHoldCount--; //重入次数--
            } else { 
                HoldCounter rh = cachedHoldCounter; //获取当前线程缓存的计数对象
                if (rh == null || rh.tid != getThreadId(current)) //最后一个读线程不是当前线程
                    rh = readHolds.get(); //获取当前线程的重入计数
                int count = rh.count; //重入次数
                if (count <= 1) { //如果小于一次,清除重入次数
                    readHolds.remove();
                    if (count <= 0) //重入次数小于0,抛异常
                        throw unmatchedUnlockException();
                }
                --rh.count; //重入次数--
            }
            for (;;) {
                int c = getState(); //获取当前锁状态
                int nextc = c - SHARED_UNIT; //读锁状态-1
                if (compareAndSetState(c, nextc)) //设置新的读锁锁状态
                    return nextc == 0; //如果设置成功,判断锁是否全部释放完毕,完毕由AQS唤醒等待线程
            }
        }
		
		/**
		*	写锁上锁
		*/
		protected final boolean tryAcquire(int acquires) {
            Thread current = Thread.currentThread(); //获取当前线程
            int c = getState(); //获取锁状态
            int w = exclusiveCount(c); //获取写锁个数
            if (c != 0) { //有线程获得到锁,
           		//w == 0表明写锁为0 同时c!=0表明有读锁,此时写锁上锁失败,
           		//写锁数量不为0,表明此时有写锁,同时当前线程不是持有独占访问权限的线程,则上锁失败
                if (w == 0 || current != getExclusiveOwnerThread()) //读锁个数为0,或者读锁个数不为0但当前线程不是持有独占访问权限的线程
                    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;
        }
		
		/**
		*	写锁释放锁
		*/
		protected final boolean tryRelease(int releases) {
            if (!isHeldExclusively())  //当前线程是否为独占访问权限的线程
                throw new IllegalMonitorStateException(); //不是,抛出异常
            int nextc = getState() - releases; //计算释放后的锁状态
            boolean free = exclusiveCount(nextc) == 0; //写锁的个数是不是为0
            if (free) //为0,全部释放完毕
                setExclusiveOwnerThread(null); //独占访问权限的线程设置为null
            setState(nextc); //设置状态
            return free; //是否唤醒等待队列的线程
        }

}

NonfairSync

	/**
	*	非公平锁
	*/
	static final class NonfairSync extends Sync {
      	//非公锁的状态下,是否应该获取写锁的方法始终返回false,非公平模式下读锁一上来就会抢锁
        final boolean writerShouldBlock() {
            return false; 
        }
        //非公平状态下的是否应该获取写锁,如果等待队列中有写锁,返回true
        final boolean readerShouldBlock() {
            return apparentlyFirstQueuedIsExclusive();
        }
    }

FairSync

	/**
	*	公平锁,判断等待队列中是否有线程等待
	*/
	static final class FairSync extends Sync {
        private static final long serialVersionUID = -2274990926593161451L;
        final boolean writerShouldBlock() {
            return hasQueuedPredecessors();
        }
        final boolean readerShouldBlock() {
            return hasQueuedPredecessors();
        }
    }

ReadLock

	public static class ReadLock implements Lock, java.io.Serializable {
       //同步器
        private final Sync sync;
        //构造方法,传入ReentrantReadWriteLock 的同步器
        protected ReadLock(ReentrantReadWriteLock lock) {
            sync = lock.sync;
        }
        //上锁的方法
        public void lock() {
            sync.acquireShared(1);
        }
        //解锁的方法
        public void unlock() {
            sync.releaseShared(1);
        }
     }

WriteLock

	public static class WriteLock implements Lock, java.io.Serializable {
        //同步器
        private final Sync sync;
        //构造方法
        protected WriteLock(ReentrantReadWriteLock lock) {
            sync = lock.sync;
        }
        //上锁
        public void lock() {
            sync.acquire(1);
        }
        //解锁
        public void unlock() {
            sync.release(1);
        }
    }

ReentrantLock主要用于读多写少的场景下,特点是一个资源可以同时被多个只读线程访问,或者一个只写线程访问,但读写操作不能同时进行。
ReentrantReadWriteLock内部维护着两个锁,一个读锁,一个写锁。读锁可以同时被多个线程获取,而写锁同一时刻只能被一个线程获取,并且一旦写锁被使用,其他所有读线程也将被阻塞。读锁是共享锁,写锁则是独占锁。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值