ReadWriteLock接口及其实现ReentrantReadWriteLock方法

  

Java并发包的锁包里的锁基本上已经介绍得差不多了,ReentrantLock重入锁是个关键,在清楚的了解了同步器aq的运行机制后,实际上再分析这些锁就会显得容易得多,这章节主讲另外一个重要的锁——ReentrantReadWriteLock读写锁。

  

ReentrantLock是一个独占锁,也就是说只能由一个线程获取锁,但如果场景是线程只做读的操作呢?这样ReentrantLock就不是很合适,读的线程并不需要保证其线程的安全性,任何一个线程都能去获取锁,只有这样才能尽可能地保证性能和效率.ReentrantReadWriteLock就是这样的一个锁,在其内部分为读锁和写锁,可以有N个读操作线程获取到写锁,但是只能有1个写操作线程获取到写锁,那么可以预见的是写锁是共享锁(aq中的共享模式),读锁是独占锁(aq中的独占模式),首先来看读写锁的接口类:

        公共接口ReadWriteLock {   锁readLock ();//获取读锁   锁writeLock ();//获取写锁   }      

可以看到ReadWriteLock接口只定义了两个方法,获取读锁和获取写锁的方法。下面是ReadWriteLock的实现类——ReentrantReadWriteLock。

  

和ReentrantLock类似,ReentrantReadWriteLock在其内部也是通过一个内部类同步实现同步器aq,同样也是通过实现同步实现公平锁和非公平锁,这一点的思路和ReentrantLock类似。在ReadWriteLock接口中获取的读锁和写锁是怎么实现的呢?

     //ReentrantReadWriteLock   最终ReentrantReadWriteLock私人。ReadLock readerLock;   最终ReentrantReadWriteLock私人。WriteLock writerLock;   最后同步同步;   公共ReentrantReadWriteLock () {   这(假);//默认非公平锁   }   公共ReentrantReadWriteLock(布尔公平){   同步=公平& # 63;新的FairSync():新NonfairSync ();//锁类型(公平/非公平)   readerLock=new ReadLock(这个);//构造读锁   writerLock=new WriteLock(这个);//构造写锁   }   ……   公共ReentrantReadWriteLock。WriteLock writeLock0{返回writerLock;}   公共ReentrantReadWriteLock。ReadLock readLock0{返回ReaderLock;}         //ReentrantReadWriteLock ReadLock美元   公共静态类ReadLock实现锁{   保护ReadLock (ReentrantReadwritLock锁){   同步=lock.sync;//最后还是通过同步内部类实现锁   }   ……//它实现的是锁接口,其余的实现可以和ReentrantLock作对比,获取锁,释放锁等等   }         //ReentrantReadWriteLock WriteLock美元   公共静态类WriteLock implemnts锁{   保护WriteLock (ReentrantReadWriteLock锁){   同步=lock.sync;   }   ……//它实现的是锁接口,其余的实现可以和ReentrantLock作对比,获取锁,释放锁等等   }      

上面是对ReentrantReadWriteLock做了一个大致的介绍,可以看到在其内部有好几个内部类,实际上读写锁内有两个锁,ReadLock WriteLock,这两个锁都是实现自锁接口,可以和ReentrantLock对比,而这两个锁的内部实现则是通过同步,也就是同步器aq实现的,这也可以和ReentrantLock中的同步对比。
  回顾一下aq,其内部有两个重要的数据结构——一个是同步队列,一个则是同步状态,这个同步状态应用到读写锁中也就是读写状态,但aq中只有一个国家整型来表示同步状态,读写锁中则有读、写两个同步状态需要记录。所以,读写锁将aq中国家的整型做了一下处理,它是一个int型变量一共4个字节32位,那么可以读写状态就可以各占16位,高16位表示读,低16位表示写。

  

 ReadWriteLock接口及其实现ReentrantReadWriteLock方法

  

  

现在有一个疑问如果状态的值5位,二进制为(00000000000000000000000000000101),如何快速确定读和写各自的状态呢?这就要用到位移运算了。计算方式为:写状态状态,0 x0000ffff读状态状态在祝辞祝辞16。写状态增加1等于国家+ 1,读状态增加1等于状态+ (1 & lt; & lt;16)。有关移位运算可以参考《<强> & lt; & lt;,祝辞,祝辞祝辞祝辞移位操作》。

  

<强>写锁的获取与释放

  

根据我们之前的经验可以得知:aq已经将获取锁的算法骨架搭好了,只需子类实现tryAcquire(独占锁),故我们只需查看tryAcquire。

     //美元ReentrantReadWriteLock同步   保护最终布尔tryAcquire (int获得){   当前线程=Thread.currentThread;   int c=getState ();//获取状态状态   int w=exclusiveCount (c);//获取写状态,即状态,0 x00001111   如果(c !=0){//存在同步状态(读或写),作下一步判断   如果(w==0 | |现在!=getExclusiveOwnerThread())//写状态为0,但同步状态不为0表示有读状态,此时获取锁失败,或者当前已经有其他写线程获取了锁此时也获取锁失败   返回错误;   如果w + exclusiveCount祝辞(收购);MAX_COUNT)//锁重入是否超过限制   把新的错误(“马克西姆锁计数超过”);   设置状态(c +收购);//记录锁状态   返回true;   }   如果(writerShouldBlock () | | ! compareAndSetState (c, c +获得))   返回错误;//writerShouldBlock对于非公平锁总是返回假,对于公平锁则判断同步队列中是否有前驱节点   setExclusiveOwnerThread(电流);   返回true;   }

ReadWriteLock接口及其实现ReentrantReadWriteLock方法