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位表示写。
现在有一个疑问如果状态的值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方法