简单说说synchronized底层原理实现详解
Sychronized原理
用法:
- 方法
- 代码块
在JDK 1.6之前,synchronized只有传统的锁机制,因此给开发者留下了synchronized关键字相比于其他同步机制性能不好的印象。在JDK 1.6引入了两种新型锁机制:偏向锁和轻量级锁,它们的引入是为了解决在没有多线程竞争或基本没有竞争的场景下因使用传统锁机制带来的性能开销问题。
锁的升级: 偏向锁->轻量级锁->重量锁
锁的映射关系存在对象头中的。32位系统上各状态如图所示:
偏向锁:
当JVM启用了偏向锁,那么新创建的对象都是可偏向状态,此时mark word里的thread id为0,表示未偏向任何线程
加锁过程:
- 当对象第一次被线程获取锁时,发现是未偏向的,那就将thread id改为当前线程id,成功继续执行同步块中的代码,失败则升级为轻量级锁
- 当被偏向的线程再次进入同步块时,发现锁偏向的就是当前线程,通过一些额外检查后就继续执行。
- 当其他线程进入同步块,发现有偏向的线程了,会进入撤销偏向锁逻辑。
解锁过程:
- 栈中的最近一条lock record的obj字段设置为null
轻量级锁:
线程在执行同步块之前,JVM会在线程的栈帧上建立一个Lock Record。其包括了一个存储对象头中的mark word的Displaced Mark Word以及一个对象头指针。
加锁过程:
- 在线程栈中创建一个Lock Record,将其obj refercence字段指向锁对象。
- 通过CAS指令将Lock Record地址放在对象头的mark word中,如果对象是无锁状态则修改成功,代表获取到了轻量级锁。如果失败进入步骤3
- 如果线程以及持有该锁了,代表这是锁重入,设置Lock Record第一部分(Displaced Mark Word)为null,起到了一个重入计数器的作用。然后结束
- 走到这一步说明发生了竞争,膨胀为重量锁。
解锁过程:
- 遍历线程栈,找到所有obj字段等于当前锁对象的Lock Record
- 如果Lock Record的Displaced Mark Word为null,代表是一次重入,将obj设为null后continue
- 如果Lock Record的Displaced Mark Word不为null,则利用CAS指令将对象头的mark word恢复成为Displaced Mark Word。如果成功,则continue,否则膨胀为重量级锁
重量级锁:
利用的是JVM的监视器(Monitor)
java会为每个object对象分配一个monitor,当某个对象的同步方法(synchronized methods )被多个线程调用时,该对象的monitor将负责处理这些访问的并发独占要求。
1.当Sychronized修饰在代码块上的时候,使用的是monitorenter指令和monitorexit指令。
monitorenter
过程如下:
- 如果Monitor的进入数为0,则该线程进入Monitor,然后进入数+1,然后该线程即为Monitor的所有者
- 如果线程已经占有了Monitor只是重新进入,则进入数+1
- 如果其他线程占有了,则线程阻塞,直到Monitor的进入数为0,在尝试获取
monitorexit
过程如下:
- 指令执行时,Monitor的进入数减一,如果进入数为0,则线程退出Monitor
- 其他被阻塞的线程可以尝试获取这个Monitor的所有权
2.Synchronize作用在方式里时,会加上一个ACC_SYNCHRONIZED 标识。当有这个标识后,线程执行将先获取Monitor,获取成功才能执行方法体。
书山有路勤为径,学海无涯苦作舟