上一篇 <<<Java基础-对象布局
下一篇 >>>Condition
AQS(AbstractQueuedSynchronizer同步器):它提供了一个FIFO队列,是用来构建锁或者其他同步组件的基础框架。
AQS是一个抽象类,主要是通过继承的方式来使用,它本身没有实现任何的同步接口,仅仅是定义了同步状态的获取以及释放的方法来提供自定义的同步组件。
常用框架
ReentrantLock、Fork/Join、CountDownLatch(计数器)、Semaphore(限流信号量)等
底层实现原理
结合CAS compareAndSwapInt实现。
执行流程图
Condition 单向链表,调用await释放锁,当前线程阻塞。
核心参数
a.Node结点 采用双向链表的形式存放正在等待的线程 waitStatus状态、thread等到锁的线程
CANCELLED,值为1,表示当前的线程被取消;
SIGNAL,值为-1,释放资源后需唤醒后继节点;
CONDITION,值为-2, 等待condition唤醒;
PROPAGATE,值为-3,工作于共享锁状态,需要向后传播,比如根据资源是否剩余,唤醒后继节点;
值为0,表示当前节点在sync队列中,等待着获取锁。
b.Head 头结点 等待队列的头结点
c.Tail 尾结点 正在等待的线程
d.State 锁的状态 0无锁、1已经有线程获取锁,默认为0
当前线程重入时State不断+1 ,当调用unlock方法时State-1,完全释放锁的情况下归零,且设置exclusiveOwnerThread=null
e.exclusiveOwnerThread 记录锁的持有
核心方法
tryAcquire ---重试获取锁
tryRelease--释放锁
acquireSharedInterruptibly---将当前线程变为阻塞状态
releaseShared ---让等待的线程,被唤醒 同时状态变为0
AQS为什么头结点是为空的
头结点存储的是获取到锁的线程,当释放锁的时候就将对象置空,方便GC回收,防止浪费内存空间。
模拟AQS的底层写法(LockSupport+CAS的结合实现)核心代码
/**
* 获取锁
*/
public void lock() {
//e=0,n=1 v=0
// 底层使用cas 修改锁的状态从0变为1 硬件层面帮助我们实现
if (acquire()) {
return;
}
// 使用cas 修改锁的状态失败 设计重试次数
Thread currentThread = Thread.currentThread();
// 如果该线程已经存在的情况下
waitThreads.add(currentThread);
for (; ; ) {
//短暂重试
if (acquire()) {
// 移除队列
waitThreads.push(currentThread);
return;
}
// 重试一次还是没有获取到锁,将当前的这个线程变为阻塞状态
LockSupport.park();
}
}
/**
* 释放锁
*/
public void unLock() {
if (exclusiveOwnerThread != Thread.currentThread()) {
throw new RuntimeException("不是当前线程在释放锁");
}
// 释放锁
if (compareAndSetState(1, 0)) {
this.exclusiveOwnerThread = null;
// 取出阻塞的线程 唤醒
Thread pollThread = waitThreads.poll();
if (pollThread != null)
// 唤醒刚才阻塞的线程
LockSupport.unpark(pollThread);
}
}
相关文章链接:
<<<多线程基础
<<<线程安全与解决方案
<<<锁的深入化
<<<锁的优化
<<<Java内存模型(JMM)
<<<Volatile解决JMM的可见性问题
<<<Volatile的伪共享和重排序
<<<CAS无锁模式及ABA问题
<<<Synchronized锁
<<<Lock锁
<<<Condition
<<<CountDownLatch同步计数器
<<<Semaphore信号量
<<<CyclicBarrier屏障
<<<线程池
<<<并发队列
<<<Callable与Future模式
<<<Fork/Join框架
<<<Threadlocal
<<<Disruptor框架
<<<如何优化多线程总结