总所周知,Java中可以通过加锁,来保证多个线程访问某一个公共资源时,资源的访问不会出问题。Java提出了两种方式来加锁,一是通过关键字加锁:synchronized,二是通过java类lock来加锁。synchronized是底层托管给JVM执行的,在java 1.6 以后做了很多优化,使用很方便,性能也很好。但本文我们详细介绍的是lock(/滑稽)(文中截取的部分源码来自Java 11版本)
Lock是JUC(java.util.concurrent)包下的一个接口,在java 1.5 版本引入的线程同步工具,用于保证多线程下安全的访问共享资源。
Lock的实现类:(本文会以ReentrantLock为例去分析)
Lock接口中的方法:
// 尝试获取锁,获取成功则返回,否则阻塞当前线程
void lock();
// 尝试获取锁,线程在成功获取锁之前被中断,则放弃获取锁,抛出异常
void lockInterruptibly() throws InterruptedException;
// 尝试获取锁,获取锁成功则返回true,否则返回false
boolean tryLock();
// 尝试获取锁,若在规定时间内获取到锁,则返回true,否则返回false,未获取锁之前被中断,则抛出异常
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
// 释放锁
void unlock();
// 返回当前锁的条件变量,通过条件变量可以实现类似notify和wait的功能,一个锁可以有多个条件变量
Condition newCondition();
多线程下访问共享资源时, 访问前加锁,访问后解锁,解锁的操作一般放入finally块中。
// 共享资源
private static int count = 0;
public static void main(String[] args) {
// 创建锁
Lock lock = new ReentrantLock();
for (int i = 0; i < 100; i++) {
// 多线程
new Thread(() -> {
lock.lock(); // 加锁
try {
count++; //操作共享资源
} finally {
lock.unlock(); // 解锁
}
}).start();
}
}
这段可能你会觉得比较突然,但是相信我,看懂AQS会帮助你更容易理解ReentrantLock。
AbstractQueuedSynchronizer 抽象队列同步器,简称AQS。
AQS维护了一个volatile的state和一个CLH(FIFO)双向队列。
state是一个由volatile修饰的int型互斥变量,0表示没有任务线程使用该资源,而大于等于1表示已经有线程正在持有锁资源。
CLH队列是内部类Node来维护的FIFO队列。
一个线程获取锁资源的时候,会判断state是否等于0(无锁状态),如果是,则把这个state更新为1,表示占用到锁。而这个过程中,如果多个线程同时做这样的操作,就会导致线程的安全性问题。因此AQS采用了CAS机制,来保证互斥变量state更新的原子性。未获得锁的线程通过Unsafe类中的park方法去进行阻塞,把阻塞的线程按照先进先出的原则放到CLH双向链表中,当获得锁的线程释放锁后,会从这个双向链表的头部去唤醒下一个等待的线程再去竞争锁。
在竞争锁资源时,公平锁要判断双向链表中是否有阻塞的线程,如果有则需要去排队等待。而非公平锁的处理方式是,不管双向链表中是否有阻塞的线程在排队等待,它都会去尝试修改state变量去竞争锁,这对链表中排队的线程来说是非公平的。
一个实现锁功能的关键成员变量Sync类型的sync,Sync继承AQS。
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {...}
Sync在ReentrantLock中有两个实现类NonfairSync和FairSync,正好对应了ReentrantLock的非公平锁、公平锁两大类型。
static final class NonfairSync extends Sync {...}
static final class FairSync extends Sync {...}
ReentrantLock默认是非公平锁实现,在实例化时可以指定选择公平锁或者非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
lock.lock()
public void lock() {
sync.acquire(1);
}
可以看到调用的是,AQS的acquire()(Sync没有重写acquire()方法)
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
这里干了三件事情:
tryAcquire:会尝试通过CAS获取一次锁。
addWaiter:将当前线程加入双向链表(等待队列)中
acquireQueued:通过自旋,判断当前队列节点是否可以获取锁
protected final boolean tryAcquire(int acquires) {
// AQS的nonfairTryAcquire()方法
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取state
int c = getState();
if (c == 0) {
// 目前没有线程获取锁,通过CAS(乐观锁)去修改state的值
if (compareAndSetState(0, acquires)) {
// 设置持有锁的线程为当前线程
setExclusiveOwnerThread(current);
return true;
}
}
// 锁的持有者是当前线程(重入锁)
else if (current == getExclusiveOwnerThread()) {
// state + 1
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
private Node addWaiter(Node mode) {
Node node = new Node(mode);
for (;;) {
// 获取末位节点
Node oldTail = tail;
if (oldTail != null) {
// 当前节点的prev设置为原末位节点
node.setPrevRelaxed(oldTail);
// CAS确保在线程安全的情况下,将当前线程加入到链表的尾部
if (compareAndSetTail(oldTail, node)) {
// 原末位节点的next设置为当前节点
oldTail.next = node;
return node;
}
} else {
// 链表为空则初始化
initializeSyncQueue();
}
}
}
final boolean acquireQueued(final Node node, int arg) {
boolean interrupted = false;
try {
for (;;) {
final Node p = node.predecessor();
// 首节点线程去尝试竞争锁
if (p == head && tryAcquire(arg)) {
// 成功获取到锁,从首节点移出(FIFO)
setHead(node);
p.next = null; // help GC
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node))
interrupted |= parkAndCheckInterrupt();
}
} catch (Throwable t) {
cancelAcquire(node);
if (interrupted)
selfInterrupt();
throw t;
}
}
lock.unlock()
public void unlock() {
// AQS的release()方法
sync.release(1);
}
public final boolean release(int arg) {
// Sync的tryRelease()方法
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;
// 修改锁的持有者为null
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
释放锁就是对AQS中的状态值State进行修改。
希望这篇文章,能对你理解锁的实现有所帮助。