1.ReetrantLock:单线程下的加锁过程

1.ReetrantLock:单线程下的加锁过程

Posted by ZhaoLe on June 24, 2019

一个简单的demo

首先通过一个Demo来展示在单线程模式下ReetrantLock加锁和释放锁的过程

1
2
3
4
5
6
7
8
9
10
//Demo
private static ReentrantLock lock = new ReentrantLock();

public static void main(String[] args) {
    lock.lock();
    lock.lock();
    System.out.println("Hello World");
    lock.unlock();
    lock.unlock();
}

通过例子+运行时候的断点,我们开始学习源码。

Lock

1
2
3
4
//ReentrantLock.java
public ReentrantLock() {
    sync = new NonfairSync();
}

首先是实例化一个ReentrantLock实例,然后可以跟着断点看看实例化具体流程。

NonfairSync->abstract Sync- abstract >AbstractQueuedSynchronizer

NonfairSync->abstract Sync

默认情况下ReentrantLock是使用非公平锁方式,也是就是默认使用NonfairSync这个类来做具体实例化工作。

实例化完后准备获取锁了,具体代码流程如下。

1
2
3
4
5
6
7
//NonfairSync.java
final void lock() {
if (compareAndSetState(0, 1))
    setExclusiveOwnerThread(Thread.currentThread());
else
    acquire(1);
}
  • 【3-4】由于是刚启动,第一次调用lock方法,所以在调用compareAndSetState直接加锁成功。state变为1,并且通过setExclusiveOwnerThread将当前线程设置为已经拥有锁的线程。业务代码第一次调用lock.lock()加锁就成功了,结束第一次调用。

  • 【6】然后紧接着在业务代码第二次调用lock.lock(),由于前一次线程加锁成功,所以此时的CAS方法会失败,断点会走到acquire(1);,具体实现方法是在NonfairSync#tryAcquire->Sync#nonfairTryAcquire

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//ReentrantLock.java
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    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;
}
  • 【3-4】获取当前线程已经线程状态,当前线程已经加锁了,所以state肯定不是0
  • 【11-16】判断当前线程是不是已经获取到锁了,如果是已经获取到锁,则对state进行累加,所以ReetrantLock对于同一个线程获多次在内部是通过累加来进行记录的。

unLock

加锁结束后继续跟进代码到释放这一快。

1
2
3
4
5
6
7
8
9
10
//AbstractQueuedSynchronizer.java
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}
  • 【3】主要是是对state做减发操作,默认传过来的release是1,如果减到state等于0则会将当前线程的锁释放掉。如果没有减完则继续保存state状态。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
      //ReentrantLock.java
      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;
      }
    
  • 【4-7】在单线程的模式下。head一直是为空的,所以unparkSuccessor方法是进不去的。后面再分析。