ReentrantLock是基于AQS(AbstractQueuedSynchronizer)类实现的同步器框架,ReentrantLock是一个互斥锁,类似于Synchronized同步锁。其实是ReentrantLock一个内部类Sync继承AbstractQueuedSynchronizer类,Sync还定义了两个子类,实现公平锁(FairSync)和非公平锁(NonfairSync) ,用到的设计模式是模板设计模式。
ReentrantLock其实是基于自旋锁 、LockSupport、CAS原子操作、queue阻塞队列实现的。
ReentrantLock加锁方法是lock,释放锁的方法是unlock。
ReentrantLock lock=new ReentrantLock(true);传递一个参数,true表示公平锁,false表示非公平锁
lock.lock(); //加锁
lock.unlock();//释放锁
//重申:加解锁主要是维护一个state状态,被volatile修饰的 ,保证多线程及时可见性
volatile int state=0;//初始值是0,表示没有线程进行加锁,大于0表示有线程已经加锁了。
阻塞队列节点Node的waitStatus状态:
SIGNAL=-1 //表示可以可被唤醒
CANCELLED=1 //表示出现异常,中断引起,需要废弃结束
CONDITION = -2 // 条件等待
PROPAGATE = -3 // 传播
0 - 初始状态Init状态
lock加锁过程:
1、当调用lock方法(实际是sync.lock)的时候,当前线程t1调用tryRequire方法,先去获取的state的值,如果state=0, 判断当前双向链表的阻塞队列是否有存在节点,如果没有,线程t1调用compareAndSetState方法(使用cas操作)尝试修改state=1,如果修改成功setExclusiveOwnerThread,把自己线程设置为exclusiveOwnerThread变量中去,表示加锁成功;如果线程t1刚进来tryRequire方法发现state!=0,
那就去判断exclusiveOwnerThread是否等于当前线程t1,如果是state需要进行加1,这种就是重入锁,也表示加锁成功;反之,就是加锁失败了。
2、调用tryRequire尝试加锁失败,那么需要将线程t1写入到双线链表的阻塞队列,调用addWaiter创建一个Node对象节点,node对象中thread是t1,pre 和next其他属性还是NULL,addWaiter里面调用enq方法使用自旋方式for (;;)生成节点并写入队列,如果head节点和tail节点没有初始化也会创建一个空的节点;循环创建最终生成后是一个:head指向一个空节点node,t1的node的pre是空节点node,tail节点指向t1的node节点。
3、t1的node节点已经子队列中,接下来调用acquireQueued方法准备对其阻塞等待获取锁。也是通过for循环自旋方式去处理,阻塞之前还是会先判断t1的node节点pre节点是不是head头结点,如果是调用tryAcquire尝试去获取下锁(因为队列先进先出,按顺序获取锁,公平锁是这样),如果还是获取到了,会setHead方法,把当前线程t1的node的thread=null,pre=null,赋值给head;否则,调用shouldParkAfterFailedAcquire去修改waitStatus,判断prev=head节点的waitStatus是否是signal,是就返回,不是调用compareAndSetWaitStatus将head节点的waitStatus改成signal。
4、head节点的waitStatus改成signal后调用parkAndCheckInterrupt阻塞,实际是调用LockSupport.park(this)阻塞线程。lock加锁成功。
unlock解锁过程:
t1执行完后需要释放锁,调用unlock方法实际是调用sync.release方法,调用tryRelease方法尝试释放锁,(必须和加锁是一个线程,exclusiveOwnerThread会和当前线程作比较,不是抛异常),用当前state-1去处理,如果state=0,设置exclusiveOwnerThread=null,然后更新state值。tryRelease释放成功,compareAndSetWaitStatus操作把head节点的waitStatus改成0,获取到head的next节点t2,对节点t2进行释放锁LockSupport.unpark(thread)。