JavaSE-多线程(13)- ReentrantLock,AQS源码
以如下实例讲解,有两个线程 t1 ,t2 ,它们同时访问 ReentrantLock5 对象的 run 方法,run 方法中打印当前正在执行的线程名,在run方法开始时对 Lock 对象加锁,访问完后释放锁:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLock5 {
private Lock lock;
ReentrantLock5(Lock lock) {
this.lock = lock;
}
public void run() {
try {
lock.lock();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " running...");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
Lock lock = new ReentrantLock();
ReentrantLock5 reentrantLock5 = new ReentrantLock5(lock);
Thread t1 = new Thread(reentrantLock5::run);
t1.setName("t1");
t1.start();
Thread t2 = new Thread(reentrantLock5::run);
t2.setName("t2");
t2.start();
}
}
ReentrantLock
先看下 ReentrantLock 重入锁的结构:
ReentrantLock 实现 Lock 接口,其中有一个重要的属性 Sync(同步器) ,ReentrantLock 获取锁,释放锁都是通过 Sync 实现的,而 Sync 类继承自 AbstractQueuedSynchronizer(AQS,抽象队列同步器)
public class ReentrantLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 7373984872572414699L;
/** Synchronizer providing all implementation mechanics */
private final Sync sync;
/**
* Base of synchronization control for this lock. Subclassed
* into fair and nonfair versions below. Uses AQS state to
* represent the number of holds on the lock.
*/
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();
}
AbstractQueuedSynchronizer (AQS)
AbstractQueuedSynchronizer 是 JUC(java.util.concurrent)的核心组成部分,AQS内部维护了一个锁同步的状态 state ,state默认为 0 表示没有线程获得锁,而 exclusiveOwnerThread 表示当前获得锁的线程,如果一个线程没有获得锁则会进入一个 first-in-first-out (FIFO) 先进先出的等待队列,head为队列头,tail 为队列尾
package java.util.concurrent.locks;
import java.util.concurrent.TimeUnit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import sun.misc.Unsafe;
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer
implements java.io.Serializable {
static final class Node {}
//队列头
private transient volatile Node head;
//队列尾
private transient volatile Node tail;
/**
* The synchronization state.
* 同步状态
*/
private volatile int state;
}
AbstractQueuedSynchronizer 父类 AbstractOwnableSynchronizer
public abstract class AbstractOwnableSynchronizer
implements java.io.Serializable {
/**
* The current owner of exclusive mode synchronization.
* 当前获得锁的线程
*/
private transient Thread exclusiveOwnerThread;
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
protected final Thread getExclusiveOwnerThread() {
return exclusiveOwnerThread;
}
}
NonfairSync (非公平锁)
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
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
Lock lock = new ReentrantLock(); 初始化时默认构建的是非公平同步器,可以通过带参数构造方法指定是否创建公平同步器。
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
lock 获取锁资源方法
ReentrantLock lock 方法实际调用的是 sync 的 lock 方法:
public void lock() {
sync.lock();
}
NonfairSync lock方法
NonfairSync lock方法 包括两个步骤:
① 自旋,将 state 状态由 0 改成 1,状态修改成功,即表示线程获得锁,将获得锁的线程通过setExclusiveOwnerThread 方法是改成当前线程
② 状态没有修改成功,调用 acquire 方法
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// AbstractQueuedSynchronizer 方法
acquire(1);
}
compareAndSetState
compareAndSetState 内部调用 unsafe.compareAndSwapInt (CAS) 方法,compareAndSwapInt 方法中有两个重要的参数,expect , update,
- expect:表示线程在修改值前期望的值(比如线程进入方法时读取到的值为0,线程在执行提交操作时也希望这个值为0,如果不是则表示该资源被其他线程修改过,那么这个线程就不能继续执行操作了)
- update:表示线程修改完后的目标值
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
acquire
AbstractQueuedSynchronizer 类方法
当一个线程没有获得锁时,就会调用 acquire 方法去获得锁,从以上 lock 方法中可以看出这一点。
public final void acquire(int arg) {
// tryAcquire 调用的具体子类的实现,这里调用的是 java.util.concurrent.locks.ReentrantLock 的 tryAcquire方法,体现了模板方法的设计模式
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
当线程A获得锁时,线程B试图去获得锁时,nonfairTryAcquire返回false,即获得锁失败,因为此时状态为 1
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
// 获取当前锁状态 0 表示没有线程获得锁
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;
}
addWaiter
将没有获得锁的线程添加到 FIFO 队列中
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;
}
enq 将没有获得到锁的线程加入等待队列的具体实现
由以下代码可以看出等待的线程加入到了队列尾部
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
// 添加一个空白的 Node 节点为头节点
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
acquireQueued
这个方法内部会循环去尝试获得锁,直到其它线程释放锁,该线程获得锁后结束执行
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
// 循环执行,直到获得锁
for (;;) {
// predecessor :前任,前辈
// 获取上一个线程节点
final Node p = node.predecessor();
// 这里拿到的节点是头节点才去尝试获得锁资源,体现出 FIFO 先进先出的思想
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);
}
}
unlock 释放锁资源方法
public void unlock() {
sync.release(1);
}
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);
}
// 释放锁资源,将状态改成 0
setState(c);
return free;
}