synchronized锁的升级过程
1. 偏向锁→轻量锁(cas自旋)→重量级锁
偏向锁: 同一个线程在没有其他线程竞争锁的情况下,可以一直复用我们的锁,不会重复获取锁。
轻量锁: 多个线程同时获取锁,只有一个线程获取锁,没有获取到锁的线程会通过CAS不断重试
重量级锁: 重试多次如果没有获取到锁,则当前线程会变成阻塞,由用户态切换到内核态。
在使用synchronized1.0版本 如果没有获取到锁的情况下则当前线程直接会变为阻塞的状态。升级为重量级锁,在后期唤醒的成本会非常高。升级为重量级锁则需要从用户态切换到内核态。
用户态与内核态区别
1、内核态(Kernel Mode):运行操作系统程序,操作硬件 (内核空间)
2、用户态(User Mode):运行用户程序(用户空间)
为了安全应用程序无法直接调用的硬件的功能,而是将这些功能封装成特定的函数。当应用程序需要硬件功能时(例如读写文件),就需要进行系统调用。当进程进行系统调用后就从用户态装换为内核态。
什么是CAS
CAS: Compare and Swap,翻译成比较并交换。 执行函数CAS(V,E,N)
CAS有3个操作数,内存值V,旧的预期值E,要修改的新值N。当且仅当预期值E和内存值V相同时,将内存值V修改为N,否则什么都不做。
保证线程安全性的问题原子性
(V,E,N)===乐观锁
V: 全局共享变量 原子类中 value值 ===0
E: 旧的预期值
N: 修改的新值
CAS:无锁(自旋控制乐观锁)
缺点:自旋会消耗的cpu的资源 cas自旋空转会引发 cpu飙高,可通过短暂线程sleep的方式解决由自旋生成的cpu飙高的问题。
底层基于unsafe类实现
手写锁的升级过程
/** * recursions+1 获取锁成功 当前线程重入+1 */ public void lock() { if(recursions==0){ return; } // 如果当前线程已经获取到锁的话,则不需要重复获取锁 直接复用 if (ownerLockThread == Thread.currentThread()) { // 锁的重入次数+1 recursions++; return; } /** * 两个线程同时执行 cas 操作 将锁的状态从0改成===11 * 最终只有一个线程修改成功 自旋 */ /** * 每个线程 获取锁重试次数 * ------不是重入 而是 重试 * */ int spinCount = 0; for (; ; ) { if (spinCount >= 3) { // 让我们当前线程变为阻塞状态 notLockThreads.offer(Thread.currentThread()); // 重量锁 LockSupport.park(); // 需要我们重试次数归0 spinCount = 0; } // 优化代码 当前线程如果已经获取到锁 则直接复用 if (lockState.compareAndSet(0, 1)) { // 当前线程获取锁成功 ownerLockThread = Thread.currentThread(); // 锁的重入次数+1 recursions==1 recursions++; return; } spinCount++; } }