![](https://csdnimg.cn/release/download_crawler_static/12235901/bg5.jpg)
在内部,AQS 维护一个共享资源 state,通过内置的 FIFO 来完成获取资源线程的排队工
作。该队列由一个一个的 Node 结点组成,每个 Node 结点维护一个 prev 引用和 next 引用,
分别指向自己的前驱和后继结点,构成一个双端双向链表。
同时与 Condition 相关的等待队列,节点类型也是 Node,构成一个单向链表。
synchronized 的实现原理
Synchronized 在 JVM 里的实现都是基于进入和退出 Monitor 对象来实现方法同步和代码
块同步,虽然具体实现细节不一样,但是都可以通过成对的 MonitorEnter 和 MonitorExit 指
令来实现。
对同步块,MonitorEnter 指令插入在同步代码块的开始位置,当代码执行到该指令时,
将会尝试获取该对象 Monitor 的所有权,即尝试获得该对象的锁,而 monitorExit 指令则插
入在方法结束处和异常处,JVM 保证每个 MonitorEnter 必须有对应的 MonitorExit。
对同步方法,从同步方法反编译的结果来看,方法的同步并没有通过指令 monitorenter
和 monitorexit 来实现,相对于普通方法,其常量池中多了 ACC_SYNCHRONIZED 标示符。
JVM 就是根据该标示符来实现方法的同步的:当方法被调用时,调用指令将会检查方法
的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取 monitor,
获取成功之后才能执行方法体,方法执行完后再释放 monitor。在方法执行期间,其他任何
线程都无法再获得同一个 monitor 对象。
synchronized 使用的锁是存放在 Java 对象头里面,具体位置是对象头里面的 MarkWord,
MarkWord 里默认数据是存储对象的 HashCode 等信息,但是会随着对象的运行改变而发生
变化,不同的锁状态对应着不同的记录存储方式。在具体优化上,从 1.6 开始引入了偏向锁、
自旋锁等机制提升性能。
什么是 CAS 操作,缺点是什么?
CAS 的基本思路就是,如果这个地址上的值和期望的值相等,则给其赋予新值,否则不
做任何事儿,但是要返回原值是多少。每一个 CAS 操作过程都包含三个运算符:一个内存地
址 V,一个期望的值 A 和一个新值 B,操作的时候如果这个地址上存放的值等于这个期望的
值 A,则将地址上的值赋为新值 B,否则不做任何操作。
CAS 缺点:
ABA 问题:
比如说一个线程 one 从内存位置 V 中取出 A,这时候另一个线程 two 也从内存中取出 A,
并且 two 进行了一些操作变成了 B,然后 two 又将 V 位置的数据变成 A,这时候线程 one 进
行 CAS 操作发现内存中仍然是 A,然后 one 操作成功。尽管线程 one 的 CAS 操作成功,但可
能存在潜藏的问题。从 Java1.5 开始 JDK 的 atomic 包里提供了一个类 AtomicStampedReference
来解决 ABA 问题。
循环时间长开销大:
对于资源竞争严重(线程冲突严重)的情况,CAS 自旋的概率会比较大,从而浪费更多
的 CPU 资源,效率低于 synchronized。
只能保证一个共享变量的原子操作:
当对一个共享变量执行操作时,我们可以使用循环 CAS 的方式来保证原子操作,但是对
多个共享变量操作时,循环 CAS 就无法保证操作的原子性,这个时候就可以用锁。