synchronized原理

127 阅读2分钟

图解

synchronized.png

详解

synchronized是一种互斥锁,一次只能允许一个线程进入被锁住的代码块。通俗点来说就是一个把门的,执行方法或代码块像是一个工厂生产蛋糕,当工厂没有看门的,大家都直接就进来抢蛋糕了,秩序就乱了,厂子要炸了呀,没人管不行。这时候就出现了保安队--synchronized关键字来看大门了,蛋糕生产车间不是随便进的了,要听保安队的,让你进就进,让你在门外等着,你就等着吧。

synchronized是一种互斥锁修饰实例方法、静态方法、代码块,这三个都是蛋糕生产车间,但是保安队管理方法不同,实例方法和静态方法,保安队都是通过生成 ACC_SYNCHRONIZED 关键字用来标识,代码块则通过monitor命令来管理(monitorenter和monitorexit指令)。

当然,工厂是Java的,所以保安队管理的一定是Object,所以,无论synchronized修饰的是方法还是代码块,对应的锁都是一个实例(对象)。保安队里面的保安都是monitor,monitor对象中存储着当前持有锁的线程以及等待锁的线程队列。

JDK1.6和JDK1.8中的synchronized

在JDK 1.6之前是重量级锁,线程进入同步代码块/方法时,monitor对象把每个线程的ID进行存储并设置MarkWord的monitor对象地址,再把阻塞的线程存储到monitor的等待队列中。这种枷锁机制是依赖于操作系统的mutex相关指令来实现的,所以会有用户态和内核态之间的切换,性能损耗比较明显,也就是我们说的重量级锁。

JDK1.8之后引入了偏向锁和轻量级锁,先说偏向锁,偏向锁就是偏向于某一个线程ID,也就是只认为这个线程才可以执行同步代码的,所以在MarkWord就只需要存储线程ID,线程进来之后直接比较线程ID,相等就执行,不相等就通过CAS尝试修改线程ID,如果修改成功,则认为可以获取到锁,就执行代码,如果修改不成功,则认为有竞争环境,就撤销当前的偏向锁,升级为轻量级锁。 轻量级锁状态下,当前线程会在栈帧下创建LockRecord,LockRecord里面存储了MarkWord信息,并且有个owner指针指向枷锁的对象,线程进入轻量级锁状态的代码时,先CAS尝试将markword指向LockRecord,若成功,则获取道轻量级锁,若失败则自旋(重试),自旋一定次数还是失败后,则升级为重量级锁。 那么,那些场景使用哪个锁呢?

只有一个线程时,偏向锁
多个线程交替执行,轻量级锁
多个线程并行是,重量级锁