ReentrantLock源码复习2022/3/28

298 阅读1分钟

一个可重入互斥锁,与使用同步方法和语句访问的隐式监视锁具有相同的基本行为和语义,但具有扩展功能。

ReentrantLock由上次成功锁定的线程拥有,但尚未解锁。当锁不为其他线程拥有时,调用锁的线程将成功地获取锁返回。如果当前线程已经拥有该锁,该方法将立即返回。可以使用isHeldByCurrentThreadgetHoldCount方法来检查。 构造函数接受布尔类型。当设置为true时,打开公平锁。

使用例子

public class Main {
    private static final Lock lock = new ReentrantLock();
    public static void main(String[] args){
        new Thread(()-> {
            test();
        },"A").start();

        new Thread(()-> {
            test();
        }   ,"B").start();
    }
    public static void test() {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName()+"获取放锁");
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
            System.out.println(Thread.currentThread().getName()+"释放锁");
        }

    }

}

源码

public class ReentrantLock implements Lock, java.io.Serializable {}
private static final long serialVersionUID = 7373984872572414699L;
/** Synchronizer providing all implementation mechanics */
private final Sync sync;


abstract static class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = -5179523762034025860L;

    /**
     * Performs {@link Lock#lock}. The main reason for subclassing
     * is to allow fast path for nonfair version.
     */
    abstract void lock();

    /**
     * Performs non-fair tryLock.  tryAcquire is implemented in
     * subclasses, but both need nonfair try for trylock method.
     */
    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;
    }

    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;
    }

    protected final boolean isHeldExclusively() {
        // While we must in general read state before owner,
        // we don't need to do so to check if current thread is owner
        return getExclusiveOwnerThread() == Thread.currentThread();
    }

    final ConditionObject newCondition() {
        return new ConditionObject();
    }

    // Methods relayed from outer class

    final Thread getOwner() {
        return getState() == 0 ? null : getExclusiveOwnerThread();
    }

    final int getHoldCount() {
        return isHeldExclusively() ? getState() : 0;
    }

    final boolean isLocked() {
        return getState() != 0;
    }

    /**
     * Reconstitutes the instance from a stream (that is, deserializes it).
     */
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        s.defaultReadObject();
        setState(0); // reset to unlocked state
    }
}
实现了AbstractQueuedSynchronizer
// 头结点,你直接把它当做 当前持有锁的线程 可能是最好理解的
private transient volatile Node head;

// 阻塞的尾节点,每个新的节点进来,都插入到最后,也就形成了一个链表
private transient volatile Node tail;

// 这个是最重要的,代表当前锁的状态,0代表没有被占用,大于 0 代表有线程持有当前锁
// 这个值可以大于 1,是因为锁可以重入,每次重入都加上 1
private volatile int state;

// 代表当前持有独占锁的线程,举个最重要的使用例子,因为锁可以重入
// reentrantLock.lock()可以嵌套调用多次,所以每次用这个来判断当前线程是否已经拥有了锁
// if (currentThread == getExclusiveOwnerThread()) {state++}
private transient Thread exclusiveOwnerThread; //继承自AbstractOwnableSynchronizer

AbstractQueuedSynchronizer 的等待队列示意如下所示

image.png

等待队列中每个线程被包装成一个 Node 实例,数据结构是链表

static final class Node {
    // 标识节点当前在共享模式下
    static final Node SHARED = new Node();
    // 标识节点当前在独占模式下
    static final Node EXCLUSIVE = null;
  
    // ======== 下面的几个int常量是给waitStatus用的 ===========
    /** waitStatus value to indicate thread has cancelled */
    // 代码此线程取消了争抢这个锁
    static final int CANCELLED =  1;
    /** waitStatus value to indicate successor's thread needs unparking */
    // 官方的描述是,其表示当前node的后继节点对应的线程需要被唤醒
    static final int SIGNAL    = -1;
    /** waitStatus value to indicate thread is waiting on condition */
    // 本文不分析condition,所以略过吧,下一篇文章会介绍这个
    static final int CONDITION = -2;
    /**
     * waitStatus value to indicate the next acquireShared should
     * unconditionally propagate
     */
    // 同样的不分析,略过吧
    static final int PROPAGATE = -3;
    // =====================================================
  
  
    // 取值为上面的1、-1、-2、-3,或者0(以后会讲到)
    // 这么理解,暂时只需要知道如果这个值 大于0 代表此线程取消了等待,
    //    ps: 半天抢不到锁,不抢了,ReentrantLock是可以指定timeouot的。。。
    volatile int waitStatus;
    // 前驱节点的引用
    volatile Node prev;
    // 后继节点的引用
    volatile Node next;
    // 这个就是线程本尊
    volatile Thread thread;

}

构造方法

非公平锁

public ReentrantLock() {
// 默认实现非公平锁
    sync = new NonfairSync();
}
// 是ReentrantLock中的一个静态内部类,并且使用final修饰,表示不可继承
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
        // 介绍:1. 如果读计数非零或写计数非零且所有者是不同的线程,则失败。
        // 2. 如果计数饱和,则失败。(这只会发生在count已经是非零的情况下。)
        //3.否则,如果该线程是可重入获取或队列策略允许这样做。如果是,更新state并设置owner。
        // 失败进入等待队列
            acquire(1);
    }
    //尝试获取锁的方法 acquire里面tryAcquire实现
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
    // 第二次尝试插队加锁
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 判断当前线程是否和持有锁线程相等 重入锁 state + 1 ,前面也说了state也代表重入次数
    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;
}
// 失败加入等待对列
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);
    return node;
}
/*
*failed 标记最终acquire是否成功, interrupted标记是否曾被挂起过。 注
意到for(;;;) 跳出的唯一条件就是if (p == head && tryAcquire(arg)) 即
当前线程结点是头结点且获取锁成功。 从这里我们应该看到,这是一个线程第
三次又想着尝试快速获取锁:虽然此时该节点已被加入等待队列, 在进行睡眠
之前又通过p == head && tryAcquire(arg)方法看看能否获取锁。 也就是说
只有该线程结点的所有 有效的前置结点都拿到过锁了,当前结点才有机会争夺
锁, 如果失败了那就通过shouldParkAfterFailedAcquire方法判断是否应该
挂起当前结点
*
*/
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)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

image.png

公平锁

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}
static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;

    //调用AbstractQueuedSynchronizer的acquire方法
   //先尝试获取锁,失败的话进入等待队列
    final void lock() {
        acquire(1);
    }

    /**
     * Fair version of tryAcquire.  Don't grant access unless
     * recursive call or no waiters or is first.
     */
    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;
    }
}
//尝试获取锁的方法,重写父类的方法
protected final boolean tryAcquire(int acquires) {
    		//获取当前线程
            final Thread current = Thread.currentThread();
    		//获取状态值
            int c = getState();
            if (c == 0) {
                //c等于0,表示当前没有线程持有锁,但是可能是上一个线程刚好释放锁
                //既然是公平锁,应该先检查队列前面有没有等待的节点,有的话先让前面的节点获取锁
                //没有的话通过CAS修改state值来获取锁,并把持有锁的线程设置为当前线程
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
    		//如果当前线程已经获取了锁,表示重入锁
    		//这里只需要把state值加1即可
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
    		//没获取到锁返回false
            return false;
        }