https://www.jianshu.com/p/497a8cfeef63// ReentrantLock构造函数传入了一个内部类 NonfairSync
NonfairSync-》Sync-》AbstractQueuedSynchronizer 继承关系
public ReentrantLock() { sync = new NonfairSync(); } //上锁也是调用NonfairSync 非公平锁 public void lock() { sync.lock(); } //进入NonefairSync类的内部 final void lock() {
if (compareAndSetState(0, 1)) //获得锁 cas setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } protected final boolean compareAndSetState(int expect, int update) { // See below for intrinsics setup to support this return unsafe.compareAndSwapInt(this, stateOffset, expect, update); } stateOffset = unsafe.objectFieldOffset (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
这里,对state的偏移量中的内存进行CAS操作,设置为1,表明第一次获取锁。AQS中,state是一个volatile变量,表示线程获取锁的次数,state为0,表示没有获取锁,state大于1,则表示重入锁获取的次数。执行完compareAndSetState第一次获取锁之后,执行setExclusiveOwnerThread,在AbstractOwnableSynchronizer类中设置成员变量Thread exclusiveOwnerThread为当前线程,表示这个线程独占了这个锁。
如果第二次调用lock()获取锁,由于内存中state不为0,cas失败,则进入else执行acquire(1);
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //没有获得锁就acquireQueued 没有获得锁就放入队列中 cas放入队列末端 selfInterrupt(); } //tryAcquire是一个抽象方法,NonfairSync中的实现如下: protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } final boolean nonfairTryAcquire(int acquires) { // 获取当前线程 final Thread current = Thread.currentThread(); // 得到state数量 state是一个用volatile修饰的整型数值 当获得锁之后会变成1 释放后又会变成0 有一个双向链表监控着这个state,里面全是线程。 int c = getState(); // 没有获取过锁 if (c == 0) { // 获取一次,cas设置state为acquires,也就是1 if (compareAndSetState(0, acquires)) { //acquire(1) // 设置拥有者线程 setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { // 增加重入次数 int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); // 设置state,这里不需要同步,因为已经是统一线程第二次获取锁,也是在本线程里获取锁,没有线程安全问题,第一次调用的state和ownerThread一定是可见的 setState(nextc); return true; } return false; }
没有获得锁就acquireQueued 没有获得锁就放入队列中 cas操作放入队列末端。为什么是双向链表呢,因为需要知道前面节点的状态,前面线程持有锁,当前节点就尝试获得锁,如果获不到就阻塞,等待前置节节点释放后就叫醒后面的线程,否则阻塞。
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); } }