synchronized:
内置锁:每一个对象都可以用来成为锁,就是内置锁。
synchronized方法上就是当前的this对象当前类的实例。
synchronized修饰 普通方法 静态方法 (当前的class字节码对象) 修饰代码块(括号里面的对象或者类的字节码对象)。
--------------------------t4-----------------理论-----------------------
同步代码块其实是基于进入和退出Monitor对象,同步方法其实也可以用这个实现。
// monitorenter
synchronized (Sequence.class) {
if(value > 0) {
return value;
} else {
return -1;
}
}
// monitorexit
如何看字节码:
1.首先进入t3
2.javap -verbose XXX.class
3.
--------------
锁存在哪里?
任何一个对象都有一个monitor与之关联。被持有之后就在锁定的状态的。
存在对象头中(书上写的)MARKWORD里面,任何对象都有一个monitor与之关联,被线程持有就处于锁定状态。
数组和非数组。
对象头(两个字节,数组是三个字节)中的信息:
Mark Word:存对象的hashcode值(这个也是存在对象头里面的)和锁信息 ,分代年龄段。
Class Metadata Adsress(指向类型的地址)
数组:ArrayLength数组长度
-------------
锁的级别和轻重:无锁 偏向锁 轻量级锁 重量级锁
--------------
偏向锁:锁是一个线程多次获得
当一个线程访问同步块获得锁时候,会在对象头和栈帧中的锁记录里储存偏向线程的ID。
再次进入的话找有没有这个偏向锁,如果失败则:
测试是否是偏向锁(判断锁的标志位):
是:尝试cas将对象头的偏向锁指向当前的线程
否:cas竞争锁
偏向锁:获取锁和释放锁会浪费资源。很多情况竞争锁不是多个线程而是一个线程不断的重入的。
MarkWord:线程的id和锁的标志位是否是偏向锁。
撤销:等竞争出现才释放锁。只有一个线程访问同步代码块的情景。
--------------
轻量级锁
加锁:线程在执行同步块之前,JVM会现在当前线程的栈帧中创建用于储存锁记录的空间,并将对象头的Mark Word复制到锁记录中。然后尝试cas将对象头的MarkWord替换为指向锁记录的指针。 成功:获得锁 失败:自旋获取锁。
解锁:复制的东西替换回对象头,失败,表示当前锁存在竞争,则膨胀为重量级锁。不会降级。
--------------------------t4-----------------jvm层面------
注:MarkWord的详解https://blog.csdn.net/dufufd/article/details/81985236
----------------
偏向锁:竞争少的情况下,单线程,惰性释放,对象头和栈帧的锁记录里存储偏向的线程的ID。
轻量级锁:在执行同步块之前在当前线程的栈帧中创建存储锁记录的空间,并将对象头的MarkWord复制到锁记录中(修改)。释放锁失败膨胀为重量级锁,重量级锁不会恢复到轻量级锁。
偏向锁:只有一个线程访问。
轻量级锁:追求响应时间,同步块执行速度快的的时候。
重量级锁:最求吞吐量,同步块执行时间长的时候。