java.util.concurrent中提供了许多同步器,比如常用的ReentranLock,ReentrantReadWriteLock ,Semaphore和CountDownLatch。他们都依赖于AbstractQueuedSynchronizer类提供的处理。
ReentranLock
ReentranLock是重入锁,和Synchronized类似。最大不同是synchronized是基于JVM层面实现的,而Lock是基于JDK层面实现的。
- 使用方法:
ReentrantLock lock = new ReentrantLock();
lock.lock();
lock.unlock();
- 涉及到的类:
- 公平锁和非公平锁
ReentranLock分为公平锁和非公平锁,区别就在得到锁的机会是否和排队顺序相关。
如果锁被另一个线程持有,那么申请锁的其他线程会被挂起等待,加入等待队列。先调用lock函数被挂起等待的线程会排在等待队列的前端,后调用的就排在后边。如果此时,锁被释放,需要通知等待线程再次尝试获取锁。
公平锁会让最先进入队列的线程获得锁。而非公平锁则会在lock时尝试获取锁,所以可能会导致后来的线程先获得了锁,这就是非公平。
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
AbstractQueuedSynchronizer
AQS 是AbstractQueuedSynchronizer的简称。它提供了一个基于FIFO队列,可以用于构建锁或其他相关同步的基础框架。
该同步器利用了一个int来表示状态,它提供能够实现大部分同步需求的基础。在多线程环境中对状态的操纵必须确保原子性,因此对于状态的处理,使用同步器提供的以下三个方法对状态进行操作:
java.util.concurrent.locks.AbstractQueuedSynchronizer.getState()
java.util.concurrent.locks.AbstractQueuedSynchronizer.setState(int)
java.util.concurrent.locks.AbstractQueuedSynchronizer.compareAndSetState(int, int)
- 加锁
ReentranLock的lock,直接调用了sync的lock方法(FairSync或NonfairSync的lock)。
// ReentranLock
public void lock() {
sync.lock();
}
// FairSync
final void lock() {
acquire(1);
}
// NonfairSync
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
AQS的acquire方法中,tryAcquire先尝试获取锁,如果得到锁,就正常执行。如果得不到锁,先执行addWaiter给当前线程创建一个节点,并将其加入等待队列,然后acquireQueued尝试获取锁。依然获取不到锁,调用selfInterrupt进行终端处理。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
- 尝试获取锁
tryAcquire在具体的同步器中实现,以FairSync的实现为例,代码如下:
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;
}
getState取得AQS中的state变量,值为0表示还未被线程占有。hasQueuedPredecessors判断当前阻塞队列上有没有先来的线程在等待(UnfairSync没有这个判断)。CAS乐观锁尝试改变独占性变量,如果成功,那么表示当前线程获得该变量的所有权,也就是获得锁成功。setExclusiveOwnerThread将本线程设置为独占性变量所有者线程。
如果该线程已经获取了独占性变量的所有权,那么根据重入性原理,将state值进行加1,表示多次lock。
其他情况都说明获取锁失败。
※compareAndSetState函数,使用CAS操作来设置state的值,而且state值设置了volatile修饰符,确保修改state的值不会出现多线程问题。
- 加入队列
addWaiter方法中调用compareAndSetTail尝试将当前线程加入到队列,如果失败在调用enq(更加复杂耗时的算法)加入队列。enq使用for循环,不停的尝试加入队列。
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
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) { //初始化
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
- 阻塞队列
acquireQueued会执行一个for循环,直到获取锁,才返回。
循环中:判断当前节点是否应该获得这个变量(在队首了)?如果是队首,tryAcquire再次尝试获取独占性变量。如果成功,返回false,正常处理(即不用进入阻塞)。如果获取失败,就调用shouldParkAfterFailedAcquire判断是否应该进入阻塞状态。如果当前节点之前的节点已经进入阻塞状态了,那么就可以判定当前节点不可能获取到锁,为了防止CPU不停的执行for循环,消耗CPU资源,调用parkAndCheckInterrupt函数来进入阻塞状态
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); //如果成工,那么就将自己设置为head
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
//调用parkAndCheckInterrupt进行阻塞,然后返回是否为中断状态
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL) //前一个节点在等待通知,所以当前节点可以阻塞
return true;
if (ws > 0) { //如果前一节点是取消状态,则跳过前一节点
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
//将前一节点的状态设置为signal,返回false,
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this); //将AQS对象自己传入
return Thread.interrupted();
}
- 阻塞和中断
AQS通过调用LockSupport的park方法来执行阻塞当前进程的操作。
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);//设置阻塞对象,用来记录线程被谁阻塞的,用于线程监控和分析工具来定位
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
- 释放锁
unlock操作调用了AQS的relase方法来释放独占性变量。如果成功,那么就看是否有等待锁的阻塞线程,如果有,就调用unparkSuccessor来唤醒他们。
可重入锁的体现,只有等到state的值为0时,才代表锁真正被释放了。所以独占性变量state的值就代表锁的有无。当state=0时,表示锁未被占有,否在表示当前锁已经被占有。
调用了unpark方法后,被阻塞的线程就恢复到运行状态,就会再次执行acquireQueued中的无限for循环中的操作,再次尝试获取锁。
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) {
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;
}
private void unparkSuccessor(Node node) {
.....
//一般情况下,需要唤醒的线程就是head的下一个节点,但是如果它获取锁的操作被取消,或在节点为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);
}