-
synchronized实现原理
Java对象都是存放在堆内存中,而对象大致可以分为下列三个部分,所以锁的信息都存放在对象头中。
-
对象头
专门使用一些指针来存放各种对象类型信息,比如对象的
hasCode
,分代年龄,锁的状态等。 -
实例变量
存储对象的属性信息和父类的信息
-
填充字符
因为虚拟机要求对象的字节大小必须是8字节的整数,所以填充字符就是专门在不是8字节的整数的情况下凑齐这个整数。
-
monitor
每个对象都有一个与之关联的
monitor
,而当一个monitor
被某个线程持有后,就表示这个线程获取了这个对象锁,也就是锁定住了。在
HotSpot
虚拟机中的实现中(位于HotSpot
虚拟机源码ObjectMonitor.hpp
文件),是有两个队列,分别是_WaitSet
和_EntryList
队列,保存的类型是ObjectWaiter
,_owner
指针则是指向了拥有锁的线程,也就是线程类型,有多个线程访问的时候会把所有线程放入_EntryList
队列,当某个而线程获取monitor
后会把_owner
指向当前线程,然后把monitor
中的计数器加1,释放锁则把_owner
置为null
,并且把计数器减1,如果是调用了wait()
方法,还会把线程放入_WaitSet
队列中等待唤醒。
-
锁升级
看上面的两张对象头的图,会发现有三种不同类型的锁状态,这是在JDK1.6版本中引入的对
synchronized
锁的优化,要注意的是锁只能向上升级,也就是偏向锁->轻量锁->重量锁。-
偏向锁
有时候一段代码虽然上了锁,但是可能一段时期内只有一个线程访问,这个时候任何竞争操作都是对性能的浪费。所以虚拟机做了优化,当第一个线程进来的时候会在对象头中把锁设置为偏向锁,同时也记录了这个线程的id,往后如果每次都是这个线程来获取锁,就什么同步操作都不做,但是一旦有一个新的线程过来竞争锁,虚拟机就会修改锁的状态,锁会升级为轻量锁。
-
轻量锁
轻量锁是指在两个线程竞争的情况下,这个时候如果两个锁互相竞争的情况下,阻塞操作的性能消耗比较大,而且可能频繁交替竞争阻塞,所以直接使用自旋的方式来代替阻塞。
-
重量锁
当竞争的线程达到了3个的时候锁就会膨胀为重量锁,因为自旋也是要耗费CPU时间的,如果竞争的线程很多,自旋就得不偿失了,所以转变为重量锁,阻塞所有没有获取锁的线程。
-