> 文章列表 > Synchronized原理

Synchronized原理

Synchronized原理

1.基本特点

Synchronized具有以下特点(只考虑JDK1.8):

        1. 开始是乐观锁,如果锁发生冲突,就转化悲观锁;

        2.开始是轻量级锁,如果锁被持有的时间比较长,就会转化成重量级锁;

        3.实现轻量级锁的时候大概率用到了自旋锁策略;

        4.是一种不公平锁。

        5.是一种可重入锁。

        6.不是读写锁。

2.加锁工作过程

JVM将synchronized锁分为 无锁,偏向锁,轻量级锁,重量级锁 状态。会根据情况,进行升级。

 2.1偏向锁

第一次线程尝试加锁,优先进入偏向锁状态

偏向锁不是真的“加锁”,只是给对象头中做一个标记“偏向锁的标记”,记录这个锁属于哪一个线程。

如果后续没有其他线程来竞争该锁,那么就不用进行其他同步操作了(避免了加锁和解锁的开销)

如果后续有线程来竞争这个锁,那就取消偏向锁状态,进入一般的轻量锁状态。

偏向锁的本质是能不加锁,就不加锁,如果其他线程竞争锁,偏向锁提前一步转换为轻量级锁,对线程进行加锁。

【通俗的栗子】偏向锁就像男女两人谈恋爱,没有官宣,此时就省去了被双方家长反复询问的麻烦。如果此时有第三者插足,原来的男女就会立即的官宣,第三者只能等待。

2.2轻量级锁

随着其他线程的竞争,偏向锁状态被消除,进入轻量级锁状态(自适应的自旋锁)

此处的轻量级锁就是通过CAS来实现。

  • 通过CAS检查并更新一块内存;
  • 如果更新成功,则认为加锁成功;
  • 如果更新失败,则认为锁被占用,继续自旋式的等待(不会放弃CPU)

自旋操作是一直让CPU空转,比较浪费CPU的资源

但是此处的自旋锁不会一直的持续进行,而是到达一定的时间/重试次数后,不再自旋了。

也就是所谓的“自适应”

2.3重量级锁

如果锁竞争进一步加剧,自旋锁不能快速的获取到锁的状态,就会膨胀为重量级锁。

此处的重量级锁就是指用到内核提供的mutex

  • 执行加锁操作,先进入内核态;
  • 在内核态判定当前锁是否已经被占用;
  • 如果该锁没有占用,则加锁成功,并切换会用户态;
  • 如果该锁被占用,则加锁失败,此时线程进入锁等待队列,等待被系统唤醒;
  • 经历了一系列的沧海桑田,这个锁被其他线程释放了,操作系统也想来了这个挂起等待的线程,于是唤醒这个线程,尝试重新获取锁。

3.其他的优化

3.1锁消除

编译器+JVM会判断锁是否可以被消除,如果可以就直接消除。

什么是“锁消除”?

比如在单线程的环境下,我们仍旧使用synchronized或者使用带有锁的类(如StringBuffer),那么就会进行锁消除的操作。

3.2锁粗化

一段代码中如果出现多次对使用同一把锁进行加锁和解锁的操作,编译器+JVM会自动进行锁的粗化

在实际开发构成中,我们使用细粒度锁,是希望释放锁后其他线程获得锁并执行, 但可能没有线程抢占这个锁。这种情况下,JVM就会自动把锁粗化,避免频繁的申请锁和释放锁。

4.相关面试题

1)什么是偏向锁?

偏向锁不是真正的加锁,而只是在锁的对象头中记录一个标记(记录该锁获取所属的线程)。如果没有其他线程参与竞争锁,那么就不会真正的进行加锁操作,从而降低系统的开销。一旦有其他线程试图加锁,那么就取消偏向锁,进入轻量级锁状态。

2)synchronized的实现原理是什么?