synchronized内置锁是一种对象锁(锁的是对象而非引用),作用粒度是对象,可以用来实现对共享资源的同步互斥访问,是可重入的。
synchronized访问修饰特点:
1、修饰普通方法,持有的对象锁,锁的是实例对象
2、修饰静态方法,持有的class锁,锁的是class类的所有实例,即不同class实例对象调用synchronized static修饰的不同方法,执行结果是同步的。
3、对于同步方法块,锁是Synchronized括号里配置的对象
4、锁可重入
synchronized实现原理:
是基于JVM内置锁实现,通过内部对象Monitor是基于JVM内置锁实现,通过内部对象Monitor(监视器锁)实现,基于进入与退出Monitor对象实现方法与代码块同步,监视器锁的实现依赖底层操作系统的Mutex lock(互斥锁)实现,它是一个重量级锁性能较低。
synchronized同步获取对象锁(重量级锁)过程:
JVM基于进入和退出Monitor对象来实现方法同步和代码块同步。代码块同步是使用monitorenter和monitorexit指令实现的,monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处。任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态。
根据虚拟机规范的要求,在执行monitorenter指令时,首先要去尝试获取对象的锁,如果这个对象没被锁定,或者当前线程已经拥有了那个对象的锁,把锁的计数器加1;相应地,在执行monitorexit指令时会将锁计数器减1,当计数器被减到0时,锁就释放了。如果获取对象锁失败了,那当前线程就要阻塞等待,直到对象锁被另一个线程释放为止。
synchronized性能优化过程
jdk1.6之前是直接获取重量级锁,性能低下的原因是JVM运行是用户态下,运行重量级锁的同步代码块需要切换到内核态,在状态切换过程中及影响性能。
1.6及其之后对synchronized实现进行优化有了锁升级,重最开始new --> 偏向锁 --> 轻量级锁(自旋锁) -->重量级锁。
对象锁的四种状态
new(无锁)、偏向锁、轻量级锁(自旋锁)、重量级锁
锁升级过程(锁膨胀):
偏向锁:只有一个线程运行同步代码时使用。
轻量级锁:当开始发生竞争时使用。
竞争锁的方式:在A、B各自的线程栈中创建LockRecord对象,如果A线程抢到轻量级锁,那么锁中保存指向lockrecord的指针。
通过CAS方式获取,会占用CPU,如果有多个线程自旋等待会提高CPU的使用率,为了避免CPU占用过高,会把轻量级锁升级为重量级锁。
重量级锁:当发生激烈竞争时使用。升级为重量级锁后,操作系统实现线程之间的切换时需要从用户态转换到核心态,这个过程及其影响性能。
线程获取锁的方式:当升级为重量级锁之后,等待执行的线程会被放到队列中等待执行,处于阻塞状态,不会占用CPU,有效降低CPU使用率。
synchronized对象锁状态变化理解所需工具:jol-core(Java Object Layout)