ReentrantLock源码简单分析

ReentrantLock 能用于更精细化的加锁的Java类, 通过它能更清楚了解Java的锁机制

ReentrantLock 类的集成关系有点复杂, 既有内部类, 还有多重继承关系

在这里插入图片描述### 类的定义

public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
    /** Synchronizer providing all implementation mechanics */
    private final Sync sync;
  ..............
  • 实现了 Serializable 接口
  • 实现了Lock接口, Lock 接口中就定义常用的加锁和释放锁的方法. 是一个基本的接口, 很多的锁类都实现了这个方法
  • sync是它的重要成员变量, 加锁和解锁的操作都是通过这个变量实现的, 这个Sync 是一个静态内部类.

加锁的逻辑

ReentrantLock 的lock方法, tryLock方法和unlock方法

public void lock() {
    sync.lock();
}
public void unlock() {
    sync.release(1);
}
public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}

从上面的方法可以看出加锁的操作都是交给Sync类来实现的,下面就来看看Sync类

构造方法

/**
 * Creates an instance of {@code ReentrantLock}.
 * This is equivalent to using {@code ReentrantLock(false)}.
 */
public ReentrantLock() {
    sync = new NonfairSync();
}

/**
 * Creates an instance of {@code ReentrantLock} with the
 * given fairness policy.
 *
 * @param fair {@code true} if this lock should use a fair ordering policy
 */
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

两种构造方法其实是对应这两种锁, 公平锁和非公平锁, 公平锁是依赖FairSync 类来实现的, 非公平锁是依赖NonfairSync来实现的

NonfairSync类详解, 非公平获取锁的真正操作类

静态内部final类

/**
 * Sync object for non-fair locks
 */
static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    /**
     * Performs lock.  Try immediate barge, backing up to normal
     * acquire on failure.
     */
    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

NonfairSync类实现了虚拟类Sync 的lock 和tryAcquire 方法

而Sync 又继承了AbstractQueuedSynchronizer 虚拟类, AbstractQueuedSynchronizer 是一个很重要的类内部维护了 等待获取锁的线程队列

lock方法

加锁方法, 方法内有两个分支逻辑,

  1. 先判断是否是无锁状态并尝试加锁compareAndSetState , 如果无锁且加锁成功则把当前线程加入AQS队列中setExclusiveOwnerThread.
  2. 尝试获取锁没有成功, 就调用acquire 方法来获取锁

compareAndSetState方法判断

protected final boolean compareAndSetState(int expect, int update) {
    // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

内部使用unsafe.compareAndSwapInt方法, 这方法是native方法. 是把比较和设置两步作为原子操作的方法.

setExclusiveOwnerThread方法

这个方法就是 AbstractQueuedSynchronizer 类的方法, 左右就是把当前线程标记成获取锁的线程

acquire方法, 真正获取锁的方法.

这个是NonfairSync 父类的父类AbstractQueuedSynchronizer 类的方法

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 如果获取到锁就会跳出这个循环, 如果获取不到锁, 会一直阻塞在这里
        selfInterrupt();
}

内部一个if判断后的调用selfInterrupt方法. 重点在if判断中逻辑.

if中首先调用tryAcquire方法, tryAcquire 在AbstractQueuedSynchronizer 中 没有实现逻辑只是抛出异常, 所以具体的逻辑实在子类NonfairSync 中,

NonfairSync.tryAcquire方法

 protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }

可以看到调用了nonfairTryAcquire 方法, nonfairTryAcquire方法又在Sync类中实现

Sync.nonfairTryAcquire方法如下

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();  //获取当前线程
    int c = getState();  // 获取当前锁的计数器
    if (c == 0) {  // 计数器0,说明当前锁是空闲的
        if (compareAndSetState(0, acquires)) { //比较并获取锁  
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {  // 当前线程就是已经获取锁的线程
        int nextc = c + acquires;  // 直接计数器上加 acquires, 传入的是1 
        if (nextc < 0) //   计数器数字异常
            throw new Error("Maximum lock count exceeded");
        setState(nextc);  // 设置计数器的数值.  这里可以直接设置, 因为当前线程就是已经获取锁的线程,  可重入锁就是体现在这的
        return true;
    }
    return false; //  获取锁失败
}
  • 这个方法的作用, 就是看下锁的状态是否可以直接获取锁, 两种情况可以直接获取锁
    • 锁是空闲状态, 没有线程获取锁
    • 当前线程就是获取锁的线程, 直接计数器加值表示重复获取锁

acquireQueued(addWaiter(Node.EXCLUSIVE), arg)方法,

这个也是AbstractQueuedSynchronizer 的方法. 这个方法才是真正的自旋阻塞获取锁的方法

/**
 * Acquires in exclusive uninterruptible mode for thread already in
 * queue. Used by condition wait methods as well as acquire.
 *
 * @param node the node
 * @param arg the acquire argument
 * @return {@code true} if interrupted while waiting
 */
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;  //自旋是否的标识符
    try {
        boolean interrupted = false; 
        for (;;) {
            final Node p = node.predecessor();  //获取当前线程的上一个线程
            if (p == head && tryAcquire(arg)) {  // tryAcquire 就是尝试获取一下锁
                setHead(node);     
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
  • node.predecessor(); 就是获取当前线程的上一个线程
  • 如果当前线程获取锁成功, 就把当前线程设置成队列的头部, 并且方法返回false
  • 如果当前线程获取锁失败, 就进入shouldParkAfterFailedAcquire 方法, shouldParkAfterFailedAcquire内部会判断当前线程的上个线程的状态标记, 如果标记是<0(标识有问题) 就把上个线程移除队列, 如果标识是 SIGNAL就返回true, 如果是其他的就设置成SIGNAL 并且返回false. SIGNAL 标识线程阻塞中
  • parkAndCheckInterrupt方法会判断当前线程是否应该中断,
  • finally 中的方法 在正常获取到锁的时候回运行, cancelAcquire 中把当前线程和前面的线程都移除队列
  • 其中addWaiter(Node.EXCLUSIVE) 方法就是把当前线程封装成Node, 并且把这个Node增加在队列的尾部
private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);
    // Try the fast path of enq; backup to full enq on failure
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) { // 把当前线程 比较并设置 成线程对列的尾部
            pred.next = node;
            return node;
        }
    }
  // 设置失败就走到这里了
    enq(node);  //这个方法内部进行循环  比较并设置 成线程队列的尾部.    如果队列还是空的, 就new一个新的Node设置在头部  
    return node;
}

到这里 就是加锁逻辑全部走完

解锁的逻辑

unlock解锁方法

public void unlock() {
    sync.release(1);
}

release方法在是在AQS中实现的

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

tryRelease 解锁方法

是在Sync中实现的

protected final boolean tryRelease(int releases) {
    int c = getState() - releases;  //当前锁计数器 减去一些
    if (Thread.currentThread() != getExclusiveOwnerThread())  //如果当前线程不是获取锁的线程,直接抛出异常
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {  //此次释放后, 计数器等于0. 
        free = true;
        setExclusiveOwnerThread(null); // 设置队列中锁线程标记为null
    }
    setState(c);  // 设置新的计数器
    return free;
}
  • 释放锁的逻辑很简单

unparkSuccessor方法

在释放锁成功后, 会进行这个方法. 这个方法会把后续的线程唤醒. LockSupport.unpark(s.thread); 就是唤醒线程方法

private void unparkSuccessor(Node node) {
	// 将状态设置为同步状态
	int ws = node.waitStatus;
	if (ws &lt; 0) 		compareAndSetWaitStatus(node, ws, 0); 	// 获取当前节点的后继节点,如果满足状态,那么进行唤醒操作 	// 如果没有满足状态,从尾部开始找寻符合要求的节点并将其唤醒 	Node s = node.next; 	if (s == null || s.waitStatus &gt; 0) {
		s = null;
		for (Node t = tail; t != null &amp;&amp; t != node; t = t.prev)
			if (t.waitStatus &lt;= 0)
				s = t;
		}
	if (s != null)
		LockSupport.unpark(s.thread);
}

FairSync公平锁的逻辑

lock方法

加锁逻辑, 与NoFairSync区别是直接

final void lock() {
    acquire(1);
}

acquire方法与非公平锁的方法一样

tryAcquire方法

这个方法与NonfairSync中tryAcquire方法有区别的, NonfairSync中的tryAcquire 是调用了父类Sync中的nonfairTryAcquire方法, 感觉这里有点奇怪

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread(); 
    int c = getState();
    if (c == 0) {
        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;  
}
  • hasQueuedPredecessors方法中的逻辑很绕, hasQueuedPredecessors 判断当前线程在队列中的第二位, 是返回false, 否则返回true
  • 是第二位, 则进行compareAndSetState 方法, 比较并设置, 如果锁没有线程获取就尝试获取. 获取成功就标记当前线程为获取锁线程
  • 如果state 不是0 , 表示已经有线程获取锁了, if (current == getExclusiveOwnerThread()) 判断当前线程是否已经是获取锁的线程.
  • 如果最终还是没有获取锁成功, 就返回false

公平锁和非分公平锁的获取锁的区别

Lock方法中的区别

  1. 公平锁中lock 会直接进入acquire 方法, 会直接进入队列中获取锁
  2. 非公平锁, 会先尝试下判断当前线程是否已经获取锁, 获取锁计数器0 尝试获取下, 获取失败才会进入acquire 方法

tryAcquire方法中的区别, 这个方法才是真正的一次获取锁的方法,

  1. 公平锁在compareAndSetState之前会调用下hasQueuedPredecessors 方法, 判断下当前节点是否是第二节点. 是第二节点才会获取锁
  2. 非公平锁 没有hasQueuedPredecessors判断.

猜你喜欢

转载自blog.csdn.net/leisurelen/article/details/106061740