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) // overflowthrow 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());elseacquire(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);}
}