AQS详解 1
1.本文不讲概念,用jdk8调试一步一步搞清AQS这个东西,请耐心并跟着本文实操。
现在我们先实现下面这个类:
public class MutexLock {
static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 3601133938511592218L;
/***
* 尝试获取锁
*/
protected boolean tryAcquire(int arg) {
// 当 state 值为0时,然后就改为1,否则就返回false也不修改state值
if (compareAndSetState(0, 1)) {
// 设置当前线程为 独占线程
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
//尝试释放锁
@Override
protected boolean tryRelease(int arg) {
if (getState() == 0) {
// state 为 0 说明当前同步块中没有锁了,无需释放
throw new IllegalMonitorStateException();
}
//当前独占线程 设置为null
setExclusiveOwnerThread(null);
// 将状态变量的值设为 0,以便其他线程可以成功修改状态变量从而获得锁
setState(0);
return true;
}
// 当前线程是否被独占
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}
}
// 将操作代理到 Sync 上
private final Sync sync = new Sync();
public void lock() {
sync.acquire(1);
}
/**
* 试图释放此锁
*/
public void unlock() {
sync.release(1);
}
上面代码仿照了ReentrantLock,实现了lock和unlock方法,实际操作都是Sync这个内部类操作的。
下面我们来测试下:
public static void main(String[] args) throws InterruptedException {
// final ReentrantLock lock = new ReentrantLock();
final MutexLock lock = new MutexLock();
/// 代码A
lock.lock();
new Thread() {
public void run() {
//代码B
lock.lock();
System.err.println("==============");
lock.unlock();
};
}.start();
Scanner sc = new Scanner(System.in);
System.out.print("Please enter a string : ");
String lein = sc.next();
if (lein.equals("1")) {
//代码C
lock.unlock();
}
}
上面代码大致解释:
- 主线程加锁,然后子线程lock时拿不到锁会阻塞等待
- 主线程在控制台输入1 回车时,主线程unlock,子线程被唤醒直到执行完毕!
调试分析
1.代码A 块的执行
我们调用代码A 【lock.lock();】 方法,跟踪进到AQS类的acquire方法
执行子类的tryAcquire方法, 调试我们返回true。
返回上层函数,直接返回,到此main函数的代码A 【lock.lock();】 方法已经执行完毕!
执行完总结:
AQS的两个成员变量
state 的值 变成1。
exclusiveOwnerThread的值变成 当前main函数线程的引用。
2.代码B块的执行
同样跟踪到下面tryAcquire方法,整个方法返回false
回到上层方法,进入addWaiter
addWaiter方法如下
执行enq方法之前我们观察Node的数据结构和值如下(记住)
进入enq方法,进入第一次循环。
第二次序号循环
到此我们能想象出如下数据结构图
到此我们有了Node1节点和Node2节点
继续走代码
返回上一层,执行acquireQueued函数
acquireQueued函数的第一次循环
下面我们看shouldParkAfterFailedAcquire这个方法
上面执行完后,需要知道Node1节点的waitStatus变量变成-1了
第二次循环,上面shouldParkAfterFailedAcquire返回false,所以直接进入第二次循环
第二次进入shouldParkAfterFailedAcquire方法
返回上一层
进入parkAndCheckInterrupt方法
代码B块的执行总结
1.阻塞了当前子线程,利用LockSupport.park工具阻塞
2.在AQS内部构造了如下链表数据结构
今天就讲到这里吧,分析不清楚的敬请谅解。可以加我qq一起讨论。
老生常谈:深圳有爱好音乐的会打鼓(吉他,键盘,贝斯等)的程序员和其它职业可以一起交流加入我们乐队一起嗨。我的QQ:657455400 表演视频实例https://v.qq.com/x/page/f0517awx0x4.html
上一篇: 食物
下一篇: 你懂得胃疼的人吃什么最养胃吗