1. 概述
lock锁, 基于队列同步器AQS, 实现公平锁、非公平锁
队列同步器AQS可以阅读我这篇文章: 点击传送
实现了Lock接口:
public class ReentrantLock implements Lock
// 加锁 获取不到锁一直等待
void lock();
// 加锁 获取不到锁一直等待 等待过程可以被中断
void lockInterruptibly() throws InterruptedException;
// 尝试获取锁 获取成功返回 true
boolean tryLock();
// 尝试获取锁 等待指定的时间 获取失败返回false
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
// 解锁
void unlock();
2. 源码阅读
2.1 属性
// 队列同步器,继承了AbstractQueuedSynchronizer
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer
2.2 构造函数
构造函数:
// 默认非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
// true:公平锁 false:非公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
2.3 Sync队列同步器
2.3.1 非公平尝试获取同步状态 nonfairTryAcquire
state为0
● 直接CAS 尝试获取同步状态, compareAndSetState(0, acquires)
● 成功设置当前线程占有锁 setExclusiveOwnerThread(current);
当前线程已经获取锁:
● state下一个值 nextc = c + acquires;
● setState(nextc) 设置值
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;
}
当前线程是否持有锁
protected final boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
获取持有锁的线程
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
2.3.2 tryRelease
尝试释放同步状态:
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
// 持有锁的才可以释放同步状态
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 同步状态为0 即要释放锁了
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
2.3.3 NonfairSync、FairSync
NonfairSync 非公平的 Sync , FairSync 公平的 Sync
公平和非公平锁是在 ReentrantLock 重写 AQS tryAcquire 中实现的
● 非公平锁: lock 的时候直接尝试一下获取锁 成功就成功了 失败了才进队列排队获取
● 公平锁:tryAcquire AQS同步队列中没有数据, 即没人排队才可以获取锁
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
// 非公平锁 lock 的时候直接尝试一下获取锁 成功就成功了 失败了才进队列排队获取
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
// 非公平尝试获取同步状态
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
// 尝试获取同步状态
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// !hasQueuedPredecessors() AQS 同步队列中没有数据, 即没人排队才可以获取锁
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;
}
}
2.4 ReentrantLock 调用 Sync
public void lock() {
sync.lock();
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public void unlock() {
sync.release(1);
}
3. 测试代码
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockStudy {
static int COUNT = 0;
public static void main(String[] args) throws InterruptedException {
ReentrantLock lock = new ReentrantLock();
// 加锁
lock.lock();
System.out.println(1);
// 可重入
lock.lock();
System.out.println(2);
// 解锁
lock.unlock();
System.out.println(3);
lock.unlock();
System.out.println(4);
new Thread(() -> {
for (int i=0; i<10000; i++) {
lock.lock();
COUNT++;
lock.unlock();
}
System.out.println("end");
}).start();
new Thread(() -> {
for (int i=0; i<10000; i++) {
lock.lock();
COUNT++;
lock.unlock();
}
System.out.println("end");
}).start();;
while (COUNT != 20000);
System.out.println(COUNT);
}
}