Contents
  1. 1. 参考资料
  2. 2. 特性
  3. 3. 使用
  4. 4. lock原理
    1. 4.1. 非公平锁获取锁的步骤
    2. 4.2. 公平锁的获取方式
  5. 5. 解锁
  6. 6. AQS中Node的waitStatus

参考资料

http://cmsblogs.com/?p=2210

特性

  1. 支持公平锁和非公平锁:默认是非公平锁,性能更好,也可以设置为公平锁
  2. 非公平锁也只是有几次CAS插队的机会,而不会一直自旋,不会过多的浪费CPU资源
  3. 支持锁的可重入性,每次重入,锁的状态为+1,每次释放锁,状态为-1

使用

public class MyService {
private Lock lock = new ReentrantLock();
public void testMethod() {
lock.lock();
try {
for (int i = 0; i < 5; i++) {
System.out.println("ThreadName=" + Thread.currentThread().getName()+ (" " + (i + 1)));
}
} finally {
lock.unlock();
}
}
}

lock原理

非公平锁获取锁的步骤

  1. 第一次尝试快速获取锁,CAS的方式(compareAndSetState(0, 1))
  2. 如果获取失败,会读取当前锁的状态,如果是未锁定,则再次尝试快速获取锁,如果已经锁定,check一下,是否满足重入的条件(int c = getState(); if (c == 0) {}else{})
  3. 如果获取失败,则会将当前线程作为节点追加到AQS队列的尾部(addWaiter(Node.EXCLUSIVE))
  4. 开始进行自旋( for (;;) {}),自旋的时候会做调用3个方法
  • 判断当前节点的前节点是否是head节点,是的话,就CAS抢一次
  • 尝试标记前节点的状态为SIGNAL
  • 标记成功之后就停止自旋,安心park待命
  1. 每次自旋时会对前置节点做状态判断,并最终修改为SIGNAL状态
  • 如果前置节点的Node状态为SIGNAL,那么返回true(等待park)
  • 如果前置节点的Node状态>0,那么说明该前置节点已经取消或者异常了,则需要从AQS队列当中剔除出去,然后,继续往前找(剔除队列中异常线程)
  • 如果前置节点处于其他状态,那么就CAS操作,尝试把前置节点的状态设置为SIGNAL
  1. 前置节点状态修改成功后,就park当前线程,并且乖乖的Thread.interrupted(),停止自旋,等待被上一个节点叫醒为止。
  2. 没抢到锁的下场如下所示
    private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);
    return Thread.interrupted();
    }

完整的抢锁的一些核心代码如下

final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
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;
}
}
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;
}
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);
}
}

公平锁的获取方式

  1. 先读取锁的状态(当前锁为0且AQS队列中没有别人时,得锁;重入得锁;否则都进入排队)
  • 如果为0,多了一个判断AQS队列里面是否已经有不是自己的节点,不是的话,则CAS尝试占有锁,失败就进入排队
  • 如果不为0,则判断是否可以重入
  1. 进入排队之后,就跟非公平锁一样了,自旋,设置前置节点等待状态,等待。
    final void lock() {
    acquire(1);
    }
    public final void acquire(int arg) {
    if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
    selfInterrupt();
    }
    先读取锁的状态
    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;
    }

解锁

  1. 先把该锁的status自减,如果是重入锁的话,说明暂时还不能释放锁,如果是非重入锁,则返回true,可以释放锁
  2. 如果释放锁,先判断一下当前的状态,如果waitStatus!=0,理论上应该是处于Signal,就可以调用unpak方法,如果后续无监听结点,则无需unpark
  3. 获取到next结点, LockSupport.unpark(s.thread);
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}

AQS中Node的waitStatus

static final int CANCELLED = 1;(取消了或者被打断了)
static final int SIGNAL = -1;(表示该节点有后续节点在排队)
static final int CONDITION = -2;
static final int PROPAGATE = -3;
None of the above = 0;(表示该节点未初始化)
Contents
  1. 1. 参考资料
  2. 2. 特性
  3. 3. 使用
  4. 4. lock原理
    1. 4.1. 非公平锁获取锁的步骤
    2. 4.2. 公平锁的获取方式
  5. 5. 解锁
  6. 6. AQS中Node的waitStatus