一、自定义锁实现
我们可以先自己尝试简单地实现一下Lock接口,进而理解Lock锁的工作原理。
首先定义2个变量:lockHolderThread、waitThreadQueue
lockHolderThread:使用AtomicReference类,用于记录当前持有锁对象的线程;waitThreadQueue:使用 LinkedBlockingQueue,用于存储由于未获取到锁而被阻塞的线程;
然后继承Lock接口,实现或完成以下方法:
- boolean tryLock()
:使用CAS机制,将lockHolderThread`替换为当前线程; boolean tryUnlock():使用CAS机制,将lockHolderThread替换为空;void lock():调用tryLock(),若失败则将当前线程休眠,并加入waitThreadQueue。若成功,则将当前线程移除waitThreadQueue;void unlock():调用tryUnlock(),若成功则依次唤醒waitThreadQueue的所有线程(或只唤醒队列中的第一个线程);
public class Demo321Lock implements Lock {
AtomicReference<Thread> lockHolderThread = new AtomicReference<>();
// 阻塞线程队列
volatile LinkedBlockingQueue<Thread> waitThreadQueue = new LinkedBlockingQueue<>();
@Override
public void lock() {
boolean isCached = false;
// 如果CAS获取锁失败,则阻塞并将当前线程加入阻塞队列
while(!tryLock()){
if(!isCached){
waitThreadQueue.offer(Thread.currentThread());
isCached = true;
}else{
LockSupport.park();
}
}
waitThreadQueue.remove(Thread.currentThread());
}
@Override
public void lockInterruptibly() throws InterruptedException {}
@Override
public boolean tryLock() {
// CAS机制尝试获取锁
return lockHolderThread.compareAndSet(null,Thread.currentThread());
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
public boolean tryUnlock(){
return lockHolderThread.compareAndSet(Thread.currentThread(),null);
}
@Override
public void unlock() {
// CAS机制释放锁,并唤醒其他线程
if(tryUnlock()){
// 唤醒全部线程
/*for (Thread thread : waitThreadQueue) {
LockSupport.unpark(waitThreadQueue.iterator());
}*/
// 唤醒队列中的第一个线程
if(waitThreadQueue.iterator().hasNext()){
LockSupport.unpark(waitThreadQueue.iterator().next());
}
}
}
@Override
public Condition newCondition() {
return null;
}
}
自定义锁使用演示:可以看到基本实现了线程安全
public class Demo321 {
private static Lock lock = new Demo321Lock();
private static int count ;
public static void main(String[] args) throws InterruptedException {
new Thread(Demo321::incr).start();
new Thread(Demo321::incr).start();
new Thread(Demo321::incr).start();
new Thread(Demo321::incr).start();
Thread.sleep(5000);
System.err.println(count);
}
private static void incr(){
for (int i = 0; i < 100; i++) {
try {
lock.lock();
Thread.sleep(10);
count++ ;
}catch (Exception e){
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
二、抽象自定义Lock(模拟AQS)
基于上述自定义锁实现,现在再做一层封装,把lock()、unlock()的逻辑再抽取一层出来:
public abstract class Demo322Aqs {
// 3个核心元素:重入状态、持有锁的线程、阻塞线程队列
// 2+2个核心方法:tryLock、tryUnlock与lock、unlock
// 统计当前线程的加锁次数(可重入)
// volatile AtomicInteger state = new AtomicInteger(0);
// 持有锁的线程
volatile AtomicReference<Thread> lockHolderThread = new AtomicReference<>();
// 阻塞线程队列
volatile LinkedBlockingQueue<Thread> waitThreadQueue = new LinkedBlockingQueue<>();
// 具体的加锁、解锁交由具体的锁对象实现,这里只做抽象的定义
public abstract boolean tryLock();
public abstract boolean tryUnlock();
/**
* 加锁
*/
public void lock(){
boolean isCached = false;
// 如果CAS获取锁失败,则阻塞并将当前线程加入阻塞队列
while(!tryLock()){
if(!isCached){
waitThreadQueue.offer(Thread.currentThread());
isCached = true;
}else{
LockSupport.park();
}
}
waitThreadQueue.remove(Thread.currentThread());
}
/**
* 释放锁
*/
public void unlock(){
// CAS机制释放锁,并唤醒其他线程
if(tryUnlock()){
for (Thread thread : waitThreadQueue) {
LockSupport.unpark(thread);
}
}
}
}
锁的实现就可以做如下精简:
public class Demo322Lock implements Lock {
private volatile MyAqs sync = new MyAqs();
@Override
public void lock() {
sync.lock();
}
@Override
public void lockInterruptibly() throws InterruptedException {
}
@Override
public boolean tryLock() {
return sync.tryLock();
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
public boolean tryUnlock(){
return sync.tryUnlock();
}
@Override
public void unlock() {
sync.unlock();
}
@Override
public Condition newCondition() {
return null;
}
static class MyAqs extends Demo322Aqs {
@Override
public boolean tryLock() {
return lockHolderThread.compareAndSet(null,Thread.currentThread());
}
@Override
public boolean tryUnlock() {
return lockHolderThread.compareAndSet(Thread.currentThread(),null);
}
}
}
最终的锁使用与之前未封装时的使用无异,代码略。
在真正引入AQS这个概念之前,首先做如下理解上的定义:
lock()、unLock():其实不是真正的加锁、解锁操作,只是调用资源、释放资源的操作。tryLock()、tryUnlock():是真正给线程加锁、阻塞线程、给线程解锁的操作。
三、AQS(AbstractQueuedSynchronizer:抽象队列同步器)
至此,我们在不知不觉中,已经模拟了一个简易的AQS,真正的AQS就是自己实现了lock()、unLock()的方法(在AQS中对应的方法名是:acquire()、release()),而tryLock()、tryUnlock()(在AQS中对应的方法名是:tryAcquire()、tryRelease())。这是因为各种不同的锁对象,其获取锁、释放锁时需要执行的操作不尽相同,然而加锁时的锁定资源(阻塞其余线程)、解锁时的释放资源的操作(唤醒其他线程)都是一样的,所以就有了对应的2个abstract方法、2个final方法。
对应到ReentrantLock,内部就定义了一个Sync的静态抽象类提供了tryRelease()方法,而其2个实现类FairSync、NonfairSync则都提供了tryAcquire()方法,其UML图如下:

顺便可以看到,我们可以通过如下方式去控制开启公平锁还是非公平锁,默认启用的是非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
NonfairSync的tryAcquire()方法是直接调用了Sycn类中的nonfairTryAcquire()方法,而NonfairSync则是在nonfairTryAcquire()方法的基础上加了一个!hasQueuedPredecessors()(是否队列前面还有线程,以保证先进先出)。值得注意的是,不管在初始化ReentrantLock是否启用了公平锁,在调用ReentrantLock对应的tryLock方法时,都是非公平地去尝试获取锁(tryLock直接调用的Sync对象的nonfairTryAcquire()方法):
public class ReentrantLock implements Lock, java.io.Serializable {
……
abstract static class Sync extends AbstractQueuedSynchronizer {
……
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;
}
……
}
static final class NonfairSync extends Sync {
……
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
static final class FairSync extends Sync {
……
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;
}
……
}
}
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
……
}
至于state字段,则用于统计加锁、解锁的次数,每次加锁则state+1,解锁则state-1,只有当state=0时,则将AQS中的exclusiveOwnerThread置为null(彻底释放锁:没有线程持有当前锁对象)这样就实现了锁重入。
最后在说下这里为什么会要执行selfIterrupt()方法:
