JUC锁框架源码阅读-ReentrantLock
非公平锁
并不会严格按照队列先后顺序获取锁,可能会出现插队
阅读之前先要阅读《JUC锁框架源码阅读-AbstractQueuedSynchronizer》
类图
获取锁
main
public static void main(String[] args) { //<1>创建锁 无参构造函数默认是非公平锁 ReentrantLock reentrantLock=new ReentrantLock(); //<2>加锁 reentrantLock.lock(); }
1.非公平锁使用的是内部类ReentrantLock.NonfairSync的对象
2.获取锁实现,直接CAS修改state状态为1(直接参与竞争锁 非公平体现)如果获取失败返回获取失败,AQS后续会加入CLH队列并阻塞等待唤醒
<1>ReentrantLock构造函数
public ReentrantLock() { //可以看到创建的是内部静态类NonfairSync sync = new ReentrantLock.NonfairSync(); }
<2>lock
java.util.concurrent.locks.ReentrantLock.NonfairSync#lock
final void lock() { /** * 调用尝试父类的CAS方法将 state从0设置为1 获取锁成功调用 这里就是非公平 可能出现插队的实现,不是放入对队列 而是直接参与获取 *java.util.concurrent.locks.AbstractQueuedSynchronizer#compareAndSetState */ if (compareAndSetState(0, 1)) //<3>获取锁成功 调用父类方法设置当前持有锁线程 java.util.concurrent.locks.AbstractOwnableSynchronizer.setExclusiveOwnerThread setExclusiveOwnerThread(Thread.currentThread()); else //<4>调用父类的加锁方法acquire acquire(1); }
<3>setExclusiveOwnerThread
java.util.concurrent.locks.AbstractOwnableSynchronizer#setExclusiveOwnerThread
protected final void setExclusiveOwnerThread(Thread thread) { exclusiveOwnerThread = thread; }
<4>acquire
我们通过《AQS源码阅读》 跟代码可以看到 tryAcquire是模板模式 最终还是子类自己实现的加锁逻辑
public final void acquire(int arg) { /** *<5>tryAcquire 模板模式 是抽象方法由子类实现获取锁步骤 *addWaiter 如果加锁失败 创建节点并放入队列尾 *acquireQueued 通过新创建的节点 判断是重试获取锁。还是阻塞线程 */ if (!tryAcquire(arg) && acquireQueued(addWaiter(node.EXCLUSIVE), arg)) //发出线程中断通知 selfInterrupt(); }
<5>tryAcquire
java.util.concurrent.locks.ReentrantLock.NonfairSync#tryAcquire
protected final boolean tryAcquire(int acquires) { //<6>具体的加锁逻辑 return nonfairTryAcquire(acquires); }
<6>nonfairTryAcquire
java.util.concurrent.locks.ReentrantLock.Sync#nonfairTryAcquire
final boolean nonfairTryAcquire(int acquires) { //获取当前线程 final Thread current = Thread.currentThread(); //获取锁状态 父类方法 0表示还未被占用 大于0表示被占用 int c = getState(); if (c == 0) { //CAS尝试获取一次锁 if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } //如果大于0判断持有所得线程 是不是当前线程,因为锁是可重入的 可以重复获取 else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
指定时间的获取锁
main
//<>创建锁 无参构造函数默认是非公平锁 ReentrantLock reentrantLock=new ReentrantLock(); try {
//<1>获取锁 reentrantLock.tryLock(1,TimeUnit.SECONDS); }finally { //<>释放锁 reentrantLock.unlock(); }
1.如果获取失败返回获取失败
2.AQS后续操作自旋并阻塞指定时间
3.后续到了阻塞时间会再次自旋。判断到超时了则会直接返回false 终结自旋
4.或者别的线程释放了锁。AQS唤醒了这个线程,自旋判断没有超时。则会继续超时获取锁如果获取失败继续阻塞指定时间
<1>tryLock
java.util.concurrent.locks.ReentrantLock#tryLock(long, java.util.concurrent.TimeUnit)
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { //<2>调用的sync的 sync继承自父类的 return sync.tryAcquireNanos(1, unit.toNanos(timeout)); }
<2>tryAcquireNanos
java.util.concurrent.locks.AbstractQueuedSynchronizer#tryAcquireNanos
可以阅读《AQS源码阅读》看后续流程
public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException { //如果线程已经中断 直接抛出异常 if (Thread.interrupted()) throw new InterruptedException(); //<5>or<4>调用子类实现尝试获取锁,如果没有获取到 则调用doAcquireNanos 阻塞指定时间 return tryAcquire(arg) || doAcquireNanos(arg, nanosTimeout); }
释放锁
main
public static void main(String[] args) { //创建锁 无参构造函数默认是非公平锁 ReentrantLock reentrantLock=new ReentrantLock(); try { reentrantLock.lock(); }finally { //<1>释放锁 reentrantLock.unlock(); } }
1.首先根据父类的getExclusiveOwnerThread()当前持有锁线程 判断是不是当前线程获取 如果不是则报错
4.释放前通过state-1 如果等于0才做具体释放逻辑(可重入逻辑)
3.判断state是不是等于0如果等于0表示锁已经被释放 重复调用2次unlock。则清空持有锁线程为null setExclusiveOwnerThread(null); 返回true释放成功 后续交给AQS处理
<1>unlock
java.util.concurrent.locks.ReentrantLock#unlock
public void unlock() { //<2>调用父类的release方法 sync.release(1); }
<2>release
后续流程可以参考《AQS源码阅读》
// 在独占锁模式下,释放锁的操作 public final boolean release(int arg) { // <3>调用tryRelease子类方法,尝试去释放锁,由子类具体实现 if (tryRelease(arg)) { Node h = head; // 如果队列头节点的状态不是0,那么队列中就可能存在需要唤醒的等待节点。 // 还记得我们在acquireQueued(final Node node, int arg)获取锁的方法中,如果节点node没有获取到锁, // 那么我们会将节点node的前一个节点状态设置为Node.SIGNAL,然后调用parkAndCheckInterrupt方法 // 将节点node所在线程阻塞。 // 在这里就是通过unparkSuccessor方法,进而调用LockSupport.unpark(s.thread)方法,唤醒被阻塞的线程 if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; }
<3>tryRelease
java.util.concurrent.locks.ReentrantLock.Sync#tryRelease
protected final boolean tryRelease(int releases) { //状态-1 大于0的数字表示可重入加了多少次锁 int c = getState() - releases; //如果加锁线程非当前线程抛出异常 if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; //当c等于0表示最后一次调用unlock 则进行锁的释放 if (c == 0) { free = true; //获得锁的线程设置为null setExclusiveOwnerThread(null); } //设置state setState(c); return free; }
公平锁
加锁
严格按照阻塞队列获取,先等待先获取
main
public static void main(String[] args) { //<1>创建锁 无参构造函数默认是非公平锁 ReentrantLock reentrantLock=new ReentrantLock(true); //<2>加锁 reentrantLock.lock(); }
1.公平锁 获取锁,会先判断CLH队列里面有没有数据,如果没有则表示没有等待线程 则直接尝试获取锁
2.如果CLH有等待线程则直接加入CLH队列(公平锁的体现)
<1>ReentrantLock
public ReentrantLock(boolean fair) { //true的时候初始化的是FairSync sync = fair ? new ReentrantLock.FairSync() : new ReentrantLock.NonfairSync(); }
<2>lock
java.util.concurrent.locks.ReentrantLock.FairSync#lock
final void lock() { //<3>调用AQS的 acquire acquire(1); }
<3>acquire
具体可以参考《AQS源码阅读》
java.util.concurrent.locks.AbstractQueuedSynchronizer#acquire
public final void acquire(int arg) { /** *<4>tryAcquire 模板模式 是抽象方法由子类实现获取锁步骤 *addWaiter 如果加锁失败 创建节点并放入队列尾 *acquireQueued 通过新创建的节点 判断是重试获取锁。还是阻塞线程 */ if (!tryAcquire(arg) && acquireQueued(addWaiter(node.EXCLUSIVE), arg)) selfInterrupt(); }
<4>tryAcquire
java.util.concurrent.locks.ReentrantLock.FairSync#tryAcquire
protected final boolean tryAcquire(int acquires) { //获取当前线程 final Thread current = Thread.currentThread(); //获取锁状态 int c = getState(); //等于0表示 当前空闲状态可以尝试获取 if (c == 0) { //<5>如果在CLH队列才尝试获取 否则返回false AQS放入CLH队列阻塞 (公平锁的实现) if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } //可重入判断 else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
<5>hasQueuedPredecessors
AQS方法 遍历队列 判断当前线程是否在AQS队列中
java.util.concurrent.locks.AbstractQueuedSynchronizer#hasQueuedPredecessors
public final boolean hasQueuedPredecessors() { // The correctness of this depends on head being initialized // before tail and on head.next being accurate if the current // thread is first in queue. Node t = tail; // Read fields in reverse initialization order Node h = head; Node s; return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); }
公平锁和非公平锁的区别
公平锁,如果获取锁失败,CLH队列里面没有元素才会尝试竞争,否则直接放入CLH队尾
非公平锁,如果锁获取失败,会直接参与一次竞争,这个时候如果释放锁是有可能获取成功的 就插队了