写锁是一个独占锁,它使用了AQS中的state变量的低16位表示写锁的占有状态。如果有线程获取了读锁和写锁,再有线程申请写锁,则该线程会被挂起。另外,写锁是可重入锁,再次进入,state低16位的值加一。
首先,我们还是看写锁的lock函数,源码如下:
我们还是继续进去看看源码:
进入tryAcquire源码看看,这里tryAcquire是在内部的Sync类实现,源码如下:
该tryAcquire函数处理顺序如下:
1、变量c获取当前state的值。如果c不为0,说明读锁或写锁被某线程获取。
2、如果w=0,说明写锁为0,而c不为0。此时说明已经有线程获取读锁,直接返回false。此时再来看看是不是当前线程持有写锁的,如果是,还可以申请写锁(此时就是可重入了)。
3、如果上面情况都不满足,则进到setState函数,设置可重入次数。
4、如果c为0,说明没有线程拿到读锁和写锁,就进入最后这个if里面。来看看writeShouldBlock方法,这个方法分为公平和非公平。
公平方式源码如下,还是去看AQS的阻塞队列是否有线程,有则返回true。
非公平方式源码如下,直接返回false。
5、如果if里面的条件没满足,则进到最后setExclusiveOwnerThread来设置写锁的持有线程为当前线程。
6、如果之前tryAcquire函数返回false,则将当前线程放到AQS阻塞队列挂起。
下面来看看写锁的unlock源码。
继续跟进去看看release源码,源码如下:
这里还是看tryRelease源码,这个函数在AQS时抽象方法,这里再Sync类中实现的。下面我们来看看源码:
这个tryRelease的处理逻辑很简单分为以下几个步骤:
1、首先调用isHeldExclusively看看是不是写锁的线程调用unlock方法,如果不是就抛异常。
2、如果是写锁的线程调用unlock方法,那么就计算nextc的值,也就是减一。
3、判断写锁的值是不是为0,如果是0,那么free就是true。则设置写锁的持有线程为null。最后设置state值,返回。