总结先写前面: 后面留给你去验证
在AQS当中:
主要有五个值得关注的点:
- private volatile int state; 锁状态的标志位0 为没有上锁。大于0 表示 加锁 值标识锁重入的次数。
- cas原子操作 保证了交换锁标志位的值,以及入队列等操作在高并发条件下不会出错。
- LockSupport 提供了将线程阻塞跟唤醒的操作,避免了无谓的自旋操作。
- 队列 存储所有阻塞的等待唤醒的线程。以便在释放锁的时候使用LockSuport来唤醒它们。
- 自旋
1. NonfairSync#lock()
分析阶段
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
这里面调用了两个方法:
1> 一个cas操作 如果设置成功 那就获得锁 并设置当前持有的线程为当前线程。可以直接获得执行的权利,不用去与其他线程来竞争执行的权利
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
2> 设置 线程为持有线程
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
3> 获取锁失败参与锁竞争
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
这里先引入公平锁 非公平锁的概念:
公平锁:所有的锁都通过竞争来获取锁
非公平锁: 锁可以先尝试获取锁 ,如果获取成功就不用参与竞争,第一次获取锁失败就参与竞争。
acquire(1);acquire方法做了两件事:尝试获取锁,尝试失败就放到等待队列里面。
我们先讨论非公平锁,它的tryAcquire:
final boolean nonfairTryAcquire(int acquires) {
//拿到当前线程
final Thread current = Thread.currentThread();
int c = getState();
//当前状态是未加锁
if (c == 0) {
//acquires = 1 设置成1,尝试获取 获取不到也没事。为true 就是获取锁成功
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}//如果当前线程是执行线程。
else if (current == getExclusiveOwnerThread()) {
//每次都 + 1
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
//赋值
setState(nextc);
return true;
}
//获取锁失败
return false;
}
nonfairTryAcquire总结:非公平锁的如果当前锁未被线程持有那就先持有,如果是第二次获取当前锁state++ ,可重入的锁。
我们先分析完添加到队列的方法然后再看如何解锁将state 变为 0 的.
将当前线程创建成一个Node 并且入队列。
private Node addWaiter(Node mode) {
//排他锁Node.EXCLUSIVE,创建节点
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;
}
自旋入队列
private Node enq(final Node node) {
for (;;) {
//尾部大概率是空的
Node t = tail;
if (t == null) { // Must initialize
//头设置一个空的节点
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
添加到队列不是很复杂,但是下面的才是复杂的。
//以排他的不间断模式获取已排队的线程。>>>>>>>>>>>>>>>>
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;
}
//获取失败 然后使用LockSupport#park方法阻塞当前线程
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
//可以中断的自旋
interrupted = true;
}
} finally {
//如果获取不到 放弃获取
if (failed)
cancelAcquire(node);
}
}
2.unlock
public final boolean release(int arg) {
//尝试释放锁
if (tryRelease(arg)) {
//头结点
Node h = head;
//头节点不为空
if (h != null && h.waitStatus != 0)
//解除阻塞
unparkSuccessor(h);
return true;
}
return false;
}
尝试释放锁
protected final boolean tryRelease(int releases) {
//state --
int c = getState() - releases;
//
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//锁释放了
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
//设置state状态
setState(c);
return free;
}
从线程队列中取出线程然后LockSupport.unpark()解除被阻塞的线程。
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
//头节点是null的
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
//下一个被唤醒的节点不为空就唤醒它
if (s != null)
LockSupport.unpark(s.thread);
}