Monitor和Synchronized
Monitor
每一个java对象都可以关联一个Monitor对象,如果使用Synchronized给对象加锁(重量级)之后,该对象的markword中就设置了指向Monitor的指针
- 刚开始Monitor中的owner为null
- 当Thread-1执行Synchronized(obj)之后会把owner设置为Thread-1,只能有一个owner,把obj中的mark word保管起来
- 在Thread-1上锁中,其他线程执行Synchronized(obj),都会放到Monitor中EntryList中,线程置为阻塞状态
- 当Thread-1执行完代码快,唤醒EntryList中的线程,进行抢锁(非公平的)。
Synchronized原理
Synchronized只有锁同一个对象才能保证加锁成功。
class Test{
public synchronized void test() {
}
}
等价于
class Test{
public void test() {
synchronized(this) {
}
}
}
class Test{
public synchronized static void test() {
}
}
等价于
class Test{
public static void test() {
synchronized(Test.class) {
}
}
}
Synchronized自从jdk 1.5后进行了优化,会有锁升级的过程。
轻量级锁
轻量级锁的使用场景:如果一个对象虽然有多线程要加锁,但加锁的时间是错开的(也就是没有竞争),那么可以使用轻量级锁来优化。
- 首先线程的栈帧会创建一个锁记录(lock record)对象,内部存储lock record地址和指向锁定对象的指针(Object reference)。
- 让锁记录中的Object reference指向锁定对象obj,并尝试cas交换obj中的mark word
- 如果cas成功,obj中的mark word被替换成(lock record 地址 00)的形式,而栈帧中的lock record变为obj中mark word 的值(例如,hashcode,分代年龄)
- 如果cas失败分为两种情况
- 如果别的线程持有obj的轻量级锁,这就表明有竞争,进入锁升级过程
- 如果是自己执行了Synchronized重入,那么就再加一条lock record作为重入的计数
- 退出Synchronized代码块时,如果有取值为null的锁记录lock record,代表有重入,这时重置锁记录,表示重入计数减一
- 退出Synchronized代码块时lock record取值不为null,就cas将obj的markword还原。
- cas成功,解锁成功
- 失败 说明轻量级锁进行了所升级,升级到了重量级锁,进入重量级锁流程。
重量级锁
重量级锁就用上述 Monitor执行过程。
-优化重量级锁
重量级锁竞争的时候,还可以使用自旋来进行优化,如果当前线程自旋成功(即这时候持锁线程已经退出了同步块,释放了锁),这时当前线程就可以避免阻塞。
Threa-1上锁后,Thread-2这时候获取Monitor,先自旋重试,而不是直接进入到EntryList中。自旋多次还不能获取锁,就进入到EntryList变为阻塞状态。
偏向锁
jdk1.5以后,偏向锁是默认的,Thread-1第一次获取锁时会cas将obj中的markword变为thread-1的线程ID。那么下回Thread-1在获取琐时,发现markword中的id是他自己,就不用cas操作了,大大降低了延迟。但是偏向锁没有其他地方存放hashcode(轻量级锁是保存在线程的栈帧中,重量级锁是保存在monitor中),当用户调用hashcode会导致偏向锁被撤销。