1.AQS 原理
AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。AQS是将每条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node)来实现锁的分配。
看个AQS(AbstractQueuedSynchronizer)原理图:
AQS使用一个int成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作。AQS使用CAS对该同步状态进行原子操作实现对其值的修改。
private volatile int state;//共享变量,使用volatile修饰保证线程可见性
状态信息通过protected类型的getState,setState,compareAndSetState进行操作
//返回同步状态的当前值
protected final int getState() {
return state;
}
// 设置同步状态的值
protected final void setState(int newState) {
state = newState;
}
//原子地(CAS操作)将同步状态值设置为给定值update如果当前同步状态的值等于expect(期望值)
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
2.简单介绍AQS的原理,下面我们基于AQS、CAS、LockSupport手写一把锁。关于CAS的介绍可以移步本人博客参考(基于CAS手写一把锁_coder86的博客-优快云博客)
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;
public class MyLock {
/**
* 锁的状态 0===没有线程获取到锁 1表示已经有线程获取到锁
*/
private AtomicInteger lockState = new AtomicInteger(0);
/**
* 获取到锁的线程
*/
private Thread getLockThread = null;
/**
* 没有获取到锁的线程
*/
private ConcurrentLinkedDeque<Thread> concurrentLinkedDeque = new ConcurrentLinkedDeque<Thread>();
/**
* 获取锁
*/
public void lock() {
acquire();
}
public boolean acquire() {
for (; ; ) {
System.out.println(Thread.currentThread().getName()+"cas操作");
if (compareAndSet(0, 1)) {
// 获取锁成功
getLockThread = Thread.currentThread();
return true;
}
// 获取锁失败
Thread thread = Thread.currentThread();
concurrentLinkedDeque.add(thread);
// 阻塞
LockSupport.park();
}
}
private boolean compareAndSet(int expect, int update) {
return lockState.compareAndSet(expect, update);
}
/**
* 释放锁
*/
public boolean unLock() {
if (getLockThread == null) {
return false;
}
if (getLockThread == Thread.currentThread()) {
boolean result = compareAndSet(1, 0);
if (result) {
// 公平锁唤醒:
Thread first = concurrentLinkedDeque.getFirst();
System.out.println(first.getName()+",被唤醒");
LockSupport.unpark(first);
return true;
}
}
return false;
}
}
编写测试类验证
public class Test07 {
public static void main(String[] args) throws InterruptedException {
MyLock myLock = new MyLock();
myLock.lock();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "start");
myLock.lock();
System.out.println(Thread.currentThread().getName() + "end");
}).start();
Thread.sleep(1000);
myLock.unLock();
}
}
运行过程:主线程先获取到锁,子线程未获取到锁进入阻塞状态,同时子线程对象被放入队列,1s后主线程释放了锁,执行方法LockSupport.unpark(Thread t)唤醒子线程,子线程重新获取到了锁,得以继续执行。
AQS核心思想就是多个线程争抢一把锁,同一时刻只有一个线程能获取到锁,其他线程进入阻塞状态,同时阻塞的线程被存到一个队列中,当锁被释放时,从队列中取出线程对象,LockSupport.unpark(Thread t);表示将线程从锁阻状态变成就绪状态。