java高并发类lock,Java高并发系列之ReentrantReadWriteLock源码分析(一)

实际上,在多线程并发环境下,读数据不会对数据造成修改,因此不需要加锁就可以。在ReentrantReadWriteLock类中,将读操作和写操作拆分为两把锁,分别是读锁:ReadLock和写锁:WriteLock。这种读写分离的操作,运行多线程同时读取数据,在写操作的时候,才是独占的。

我们先来看看ReentrantReadWriteLock的类图,了解其结构。这样我们看源码也能一目了然,印象也会深刻。

eaaa017f5ba9d3e5e63e6e47c70de07e.png

其实,这个类图可以看出,ReentrantReadWriteLock实现了ReadWriteLock和Serializable接口。其内部有个Sync类继承自AQS,同时这个类需要实现公平锁和非公平锁,因此Sync有两个子类:FairSync和NonfairSync类。

这里说明一下,ReentrantReadWriteLock里面有两个内部类:ReadLock和WriteLock,是因为要实现ReadWriteLock的接口的。在ReadWriteLock接口中定义了ReadLock和WriteLock的方法用于返回读锁和写锁。

ReentrantReadWriteLock的实现和之前的ReentrantLock有点像。首先,我们来看看其构造函数。其空参数的构造函数默认是使用内部的非公平锁,还可以传入参数,指定创建公平或非公平锁,源码如下所示。

a7d868c3770f77e1ba737425cbaee360.png

再扯远一点,在ReentrantReadWriteLock中有个方法:boolean isFair(),这个类似于Netty框架中判断一个Handler是inBound还是OutBound类型,也是这样判断的。源码如下:

036955b21d2e4800e10049533992c647.png

下面进入正题,下面来看读锁的源码。读锁中最重要的两个方法就是lock和unlock方法。下面我们先来看看lock方法源码。

88b5ac6fcf8895e150981d25b9ae81d0.png

这个和之前ReentrantLock实现类似,由于读锁支持多线程同时访问,因此这里使用AQS中的共享锁。我们继续跟进去看看acquireShared()源码。

3db2b0442cbce65ff21e83c6c9fe82c6.png

其中,tryAcquireShared()需要AQS的子类去实现,这里在Sync类中实现了。而doAcquireShared方法是在AQS中实现的。下面我们来看看tryAcquireShared源码。

3348c4a54539a94e455670fea02bf53b.png

这里主要分为以下几步:

1、首先获取State的值,然后调用exclusiveCount方法来判断写锁是否占用 ,当该方法返回值不为0,就说明写锁被占用。这里如果当前线程获取写锁,那么也是允许获取读锁的。

2、获取读锁的次数,也就是调用sharedCount方法进行计算。这里注意,写锁占用了State变量的低16位,读锁占用高16位。

3、这里readShouldBlock方法在Sync中是抽象方法,需要FairSync和NonFairSync去实现。

baf8380d34404a41da32ab6c16a90eb7.png

如果是公平锁,实现如下。原理是去查看AQS的阻塞队列,如果队列里面有挂起的线程,那么返回true,那么上面的tryAcquireShared函数直接进入最后fullTryAcquireShared去自旋获取锁。

68cf350a47797ca1fdd128413a73c659.png

如果是非公平锁,实现如下。其原理是判断队列中是否有元素在获取写锁。

afee56427486cd228a6689f9bb8f34e8.png

88db49d0fd52b4d4c7fb28941f5ab1b4.png

4、继续回到tryAcquireShared函数,如果成功获取读锁,那么如果r=0,那就是第一次获取读锁,这里用firstReadHoldCount来记录同一个线程读的次数。HoldCount用于记录其他线程的读次数。

下面来看看unlock的源码。

ff9c38d8c54b07cfa710814e38004381.png

进入releaseShared源码看看。

5cb407aec5d97676eceeca97f8f9c2ef.png

这里tryReleaseShared需要AQS的子类实现,这里在Sync中实现。该方法的主要作用是在for(;;)里面,用CAS操作设置state的值为nextc,nextc也就是减去一个读单位的值。如果nextc为0,说明当前没有线程占用读锁,那么返回true。然后releaseShared函数进入doReleaseShared,去唤醒其他因为写操作阻塞的线程(比如其他线程获取写锁,那么读操作和写操作不能同时进行,读操作线程会阻塞)。

a7396a1a6155c09b7d3fb6887603fdda.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值