基本概念

  • 原子操作:不可中断的一个或一系列操作。JAVA使用锁和循环(自旋)CAS实现。
  • CAS的缺点:1.ABA问题(使用版本号解决);2.循环开销大;3.只能保证一个共享变量的原子操作。
  • CAS定义:CAS机制当中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B。

上下文切换:任务从保存到再加载的过程。(多线程调度会切换到内核态)

  1. 内核态:cpu可以访问内存的所有数据,包括外围设备,例如硬盘,网卡,cpu也可以将自己从一个程序切换到另一个程序。
  2. 用户态:只能受限的访问内存,且不允许访问外围设备,占用cpu的能力被剥夺,cpu资源可以被其他程序获取。所有程序运行在用户态,但IO等操作切换到内核态。

减少上下文切换:无锁并发编程、CAS算法、使用协程。
image.png

一个线程拥有多个【协程】,协程是特殊的函数,由程序控制,只在用户态运行。

  • 多个协程是串行执行的,只能在一个线程内运行,没法利用CPU多核能力。
  • 协程与进程一样,切换是存在上下文切换问题的

volatile :轻量级,保证了共享变量的“可见性”,但其无原子性。
synchronized :重量级,是悲观锁、非公平锁、重入锁。

对象头与锁升级

对象头12B(3字节/8B),其中锁状态存放在Mark Word(4B)中。对象头的MarkWord如下:

锁状态 25bit 4bit 1bit是否为偏向锁 2bit锁标志
无锁状态 hash code 对象年龄 0 01
轻量级 指向栈中锁记录的指针 00
重量级 指向互斥量(重量级锁)的指针 10
GC 11
偏向锁 线程ID、Epoch 年龄 1 01

锁升级

定义:为了减少获得锁和释放锁的性能开销。
升级路线:无锁状态->偏向锁->轻量级锁->重量级锁。
优点与缺点

优点 缺点 适用场景
偏向锁 加解锁无太多开销 有锁竞争的话会有锁撤销的开销 只有一个线程访问同步块的情况
轻量级锁 竞争的线程不会阻塞 自旋占用CPU 追求响应时间或同步块执行速度非常快
重量级锁 不会自旋,避免占用CPU 线程阻塞,响应时间慢 追求吞吐量或同步块执行时间较长

1. 偏向锁

偏向锁:测试Mark Word里是否存在指向当前线程的偏向锁;如果是则获得锁,否则检查标志位是否为1。若为1则使用CAS将偏向锁指向当前线程,否则使用CAS竞争锁。
锁的撤销:当遇到竞争才释放锁,此时有较大的开销。首先暂停有偏向锁的进程,检查其是否存活,若不存活则设为无锁状态,否则被暂停的进程会先执行然后重设偏向锁。

2. 轻量级锁

  1. 加锁:线程执行同步块之前,会在当前线程的栈帧中创建用于存储锁记录的空间并将对象头中的Mark Word复制到锁记录。然后尝试CAS替换Mark word为指向锁记录的指针;如果成功则获取到锁,否则进入自旋。
  2. 解锁:CAS替换刚刚拷贝的Mark Word回对象头,如果成功表示没有竞争;如果失败则发生锁膨胀