参考文章:https://cloud.tencent.com/developer/article/1031633
1.synchronized基本用法
修饰在方法上,修饰在静态方法和成员方法是不一样的,修饰在静态方法上时,使用的锁对象是当前类的class的对象锁,修饰在成员方法上则是使用的是对象实例的对象锁所有两者是不会冲突的,同步方式是通过方法中的access_flags中设置ACC_SYNCHRONIZED标志来实现
修饰在代码块上,是使用一个对象实例的对象锁当成锁对象,同步代码块是通过monitorenter和monitorexit来实现。
2.是怎么实现加锁的
其实每个对象包含对象头,实例数据,对齐填充物,在不是锁对象时,对象头存放的时hashcode和对象分代年龄(改年龄达到一定值时会放进老年代),在对象成为锁对象时,会随着锁的变化而改变。
偏向锁
是在只有单个线程执行该同步代码块或者同步方法时,锁进入偏向状态,记录线程id和改变锁标志位,如果出现第二个线程执行则锁升级为轻量级锁。
轻量级锁
轻量级锁是在多个线程执行,但是没有相交的时间点同时执行,都是错开执行的,这样锁就会维持为轻量级锁,指向锁记录的指针(线程会创建一个锁记录Lock record然后对象头保存这个锁记录指针)即是有可能同时执行,获取轻量级锁失败时,也不会立马膨胀成重量级锁而是让线程自旋CAS操作,自旋达一定次数还未拿到锁则膨胀成重量级锁
重量级锁
重量级锁是通过monitor(监听器)去实现的,每个对象都会有一个这样的monitor,所有每个对象都可以成为锁对象,monitor中owner中如果有值,则说明已经有线程拥有这个锁了。
ObjectMonitor() {
_header = NULL;
_count = 0; //记录个数
_waiters = 0,
_recursions = 0;
_object = NULL;
_owner = NULL;
_WaitSet = NULL; //处于wait状态的线程,会被加入到_WaitSet
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ; //处于等待锁block状态的线程,会被加入到该列表
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
}
锁消除
就是在编译阶段发现不存在竞争情况,比如一个StringBuffer是局部变量,没有线程安全问题则会在编译时把锁去掉提高代码执行效率。