synchronized底层原理 及 优化方案

1.对象锁 (monitor) 机制
执行同步代码块时首先要执行 monitorenter 指令,退出同步代码块时要执行 monitorexit 指令.
所以 使用Synchronized进行同步,其关键就是必须要对对象的监视器monitor进行获取,当线程获取monitor后才能继续往下执行,否则就只能等待。而这个获取的过程是互斥的,即同一时刻只有一个线程能够获取到monitor。
一个monitorenter指令以及多个monitorexit指令。确保所获得的锁在正常执行路径,以及异常执行路径上都能够被解锁。

关于monitorenter和monitorexit的作用 ,可以抽象的理解为每个锁对象拥有一个锁计数器,和一个指向持有该对象锁的线程的指针.
①当执行 monitorenter 时,如果目标锁对象的计数器为 0,那么说明它没有被其他线程所持有。在这个情况下,Java 虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加 1。
在目标锁对象的计数器不为 0 的情况下,如果锁对象的持有线程是当前线程,那么 Java 虚拟机将其计数器加 1,否则需要等待,直至持有线程释放该锁。

当执行 monitorexit 时,Java 虚拟机则需将锁对象的计数器减 1。当计数器减为 0 时,那便代表该锁已经被释放掉了。

之所以采用这种计数器的方式,是为了允许同一个线程重复获取同一把锁
举个例子,如果一个 Java类中拥有多个 synchronized 方法,那么这些方法之间的相互调用,不管是直接的还是间接的,都会涉及对同一把锁的重复加锁操作。因此,我们需要设计这么一个可重入的特性,来避免编程里的隐式约束

2 synchronized优化
锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级。这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率。

2.1偏向锁
使用场景:重入操作, 同一个线程获取某对象锁后,再次申请该对象锁…自始至终只有一个线程申请同一个对象锁.
偏向锁的获取
当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,以后该线程
在进入和退出同步块时不需要进行CAS操作来加锁和解锁,只需简单地测试一下对象头的Mark Word里
是否存储着指向当前线程的偏向锁。
偏向锁的撤销
偏向锁使用了一种等到竞争出现才释放锁的机制,所以当其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁。

2.2轻量级锁
使用场景:多个线程在不同的时间段请求同一把锁,同一个时间点,只有一个线程竞争对象锁.
原理:CAS.

2.3重量级锁
使用场景:同一个时间点,有多个对象同时竞争同一个对象锁.
特性:竞争失败的锁会进入阻塞状态;
原理:系统的mutex锁

synchronized底层原理 及 优化方案
synchronized底层原理 及 优化方案