【ReentrantLock源码分析】非公平锁的加锁和解锁

一、AbstractQueuedSynchronized 的三个核心成员变量

阐述一下 AQS 中的三个核心成员变量,后面源码分析流程的时候很多地方有。

  • state:表示锁的状态,0表示锁未被锁定,大于0的话表示重入锁的次数。state 成员变量被 volatile 修饰,保障了 可见性和有序性
  • head:等待队列的头节点,除了初始化只能通过setHead()方法设置值,如果head存着能保证waitStatus状态不为CANELLED。
  • tail:等待队列尾节点,只能通过equ添加新的等待节点。

二、可重入锁加锁流程

非公平可重入锁的加锁流程核心部分如下:

  • new ReentrantLock().lock()
  • NonfairSync.lock()
  • AQS.acquire()
  • NonfairSync.tryAcquire()
  • Sync.nonfairTryAcquire() 加锁成功或者重入锁
  • 否则加锁失败,入队等待,AQS.acquireQueued()
  1. CAS 获取锁,如果没有线程占用锁(state==0),加锁成功并记录当前线程是有锁线程.(state 的含义是表示已重入锁的数量)

             final void lock() {
          
          
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
    
  2. Sync.nonfairTryAcquire() 又一次尝试去获取锁(state==0),加锁成功并记录当前线程是有锁线程.

    1.             final Thread current = Thread.currentThread();
                  int c = getState();
                  if (c == 0) {
              
              
                      if (compareAndSetState(0, acquires)) {
              
              
                          setExclusiveOwnerThread(current);
                          return true;
                      }
                  }
      
    2. 如果没有获取锁成功,则 state 更新为 state+acquires,即state += 1

                  else if (current == getExclusiveOwnerThread()) {
              
              
                      int nextc = c + acquires;
                      if (nextc < 0) // overflow
                          throw new Error("Maximum lock count exceeded");
                      setState(nextc);
                      return true;
                  }
      
    3. 其中获取锁成功和更新state值成功都返回为true,否则返回为false。

  3. 入队时仍然会去尝试去加锁,即如果此时队列没有其他节点,node节点的前一个节点是头节点,就会再次尝试加锁。

    1.     final boolean acquireQueued(final Node node, int arg) {
              
              
              boolean failed = true;
              try {
              
              
                  boolean interrupted = false;
                  // 自旋
                  for (;;) {
              
              
                      final Node p = node.predecessor();
                      // 在 p 为头节点的时候就再次尝试获得锁
                      // 获取成功就不入队:failed = false
                      // 否则就会在 finally 入队
                      if (p == head && tryAcquire(arg)) {
              
              
                          setHead(node);
                          p.next = null; // help GC
                          failed = false;
                          return interrupted;
                      }
                      if (shouldParkAfterFailedAcquire(p, node) &&
                          parkAndCheckInterrupt())
                          interrupted = true;
                  }
              } finally {
              
              
                  if (failed)
                      cancelAcquire(node);
              }
          }
      

在这里插入图片描述总的聊就是非公平锁去加锁的时候会尝试去抢俩次,也可以说三次,因为第三次抢锁条件是当当前节点的前一个节点是头节点的时候,即是空的时候,这个时候才会去再次尝试抢锁,也就是后面前面没人排队购票了,然后我正好去购票了。

前俩次抢锁一个是在加锁的时候就会进行抢锁,没成功的话,调用 tryAcquire 的时候又会去进行一次抢锁,这里还包括了可重入操作,即更新 state+=1(arg)

否则最后进入队列然后将线程挂起。

三、可重入锁解锁流程

非公平的可重入锁的解锁流程核心部分如下:

扫描二维码关注公众号,回复: 17222063 查看本文章
  • new ReentrantLock().unlock()
  • AQS.release(1)
  • Sync.tryRelease(1)
  1. 判断当前线程是否是有锁线程,不是则抛出 IllegalMonitorStateException 非法监控状态异常;
  2. 对 state 的值进行减 1 之后,判断 state 的值是否为 0,为 0 则解锁成功,返回 true;
  3. 如果减 1 后的值不为 0,则返回 false。
        protected final boolean tryRelease(int releases) {
    
    
            int c = getState() - releases;
            // 判断当前线程是否是有锁线程
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
    
    
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }
  1. 如果释放锁(解锁成功)的话,去对队列首个线程挂起的进行取消挂起,也就是去唤醒它。
    public final boolean release(int arg) {
    
    
        if (tryRelease(arg)) {
    
    
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

非公平锁的解锁流程很简单,就是去尝试去更新 state 直至为 0,state -= 1(arg)。最后 tryRelease 为 true 后然后让队列头个元素唤醒(除head之外的头个元素)。

引用了
Java锁(二):AbstractQueuedSynchronizer、ReentrantLock详解
的流程图。

猜你喜欢

转载自blog.csdn.net/qq_63691275/article/details/134216133