synchronized也叫互斥锁、悲观锁、同步锁、重量级锁。当多个线程去竞争加锁时,只有一个线程能拿到,其他线程进入到一个阻塞队列,这时候就涉及到操作系统的线程调度,性能会低一些。
所以它也叫重量级锁,涉及到很多操作系统的支持,有用户态内核态的上下文切换等,耗费性能。
它本质是java生成的字节码中有一个monitor指令。本质也是对c++代码中的monitor对象加锁。


1、原子性问题。
去看CAS的compareAndSwapInt方法底层,它是native方法,即c++实现的。里面本质是两步,先比较值是否相等,然后set。我们下载openjdk源码,去找Unsafe.cpp,最后能找到我们真正调用的c++代码。最最核心的就是 Atomic::comxche.

如果是多核cpu,会使用lock compxchgq。 即多个线程使用CAS,在汇编层面,会有lock指令,会保证compxchgq的命令是加了锁的,保证并发变为串行化。 如果数据少于64bit,就对缓存行加锁,如果数据大就会变为总线锁。
所以说CAS虽然叫无锁,实际在汇编和硬件层面还是有锁的。
2、ABA问题
在比较并交换的过程中,线程1把A->B, 比较拿到的新值A等于旧值A,就更新为B,但是在这个过程中线程2做了操作,A->C, C->A. 这就是ABA问题。
解决发放,就是加一个version版本号。
一开始是无锁,有单线程来了,就升级到偏向锁,如果有多个线程来竞争,就升级为轻量级锁,如果竞争的线程多了,就升级到重量级锁。
偏向锁
hotspot作者对大量公司的代码进行试验,发现一个代码块虽然加了synchronized锁,但是大部分时间还是一个线程去运行它。如果我们还跟1.6版本之前一样,要搞一个monitor对象,搞一堆等待队列,还要接触操作系统的互斥量等,就比较浪费。所以jdk1.6之后引入了偏向锁,优化了锁机制。即当一个线程去加锁,把线程id加到锁的内部对象中存着,只要统一线程去获取锁,直接提供,不用进行其他复杂操作。

轻量级锁
有多个线程来竞争,就进行CAS自旋去获取。
重量级锁
要看内部代码,可能是自旋10次,还有自适应自旋,最后满足条件,就升级到重量级锁。
对象头中的标示
对象分为三个部分,对象头中又包含mark word部分。
mark word占用64bit

带有synchronized的代码,编译成的字节码会带有monitorenter和monitorexit,当java虚拟机执行到这个指令时,会调用对用的c++的实现,里面源码就有复杂的锁升级过程。

我们要对一个base值进行++操作。按照synchronized的方式,就是多个线程去竞争,当线程自旋的多了,锁升级为重量级锁。如果使用LongAdder,那么底层就是下面图的方式。
根据线程数量多少,LongAdder内部为维护一个cell数组,会自动扩容和缩减。大量线程去竞争,会进行分组,一部分修改cell1,一部分修改cell2等等,会后再把cell数组中的值累加。
这样的计算,比使用atomicInteger的效率还要高。
