synchronized原理

简介: synchronized原理

基本特点

结合之前总结的锁策略,我们就可以总结出,synchronized具有以下特性(jdk1.8):

1.开始时是乐观锁,如果锁冲突频繁,就转换为悲观锁.

2.开始是轻量级实现,如果锁被持有的时间较长,就转换为重量级锁.

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

4.是一种不公平锁

5.是一种可重入锁

6.不是读写锁.

加锁加工过程

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

注意基本点:锁只能升级,不能降级,非必要不加锁.

偏向锁

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

偏向锁并不是真的"加锁",只是给对象头做一个"偏向锁的标记",记录这个锁属于哪个线程.如果后续没有其它线程来竞争该锁,那么就不用进行其他同步操作了(避免了加锁解锁的开销).如果后续有其它线程来竞争该锁(刚才已经在锁对象中记录了当前锁属于哪个线程了,很容易识别当前申请锁的线程是不是之前记录的线程),那就取消原来偏向锁的状态,进入一般轻量级锁的状态,进入一般轻量级锁的状态.

偏向锁本质上相当于"延迟加锁",能不加锁就不加锁,尽量来避免不必要的加锁开销.

但是该做的标记还是得做的,否则无法区分何时需要真正加锁.

轻量级

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

此处的轻量级锁通过CAS实现(认识过程即可,CAS后面会讲)

1.通过CAS检查并更新一块内存(比如null->该线程引用)

2.如果更新成功,则认为加锁成功

3.如果更新失败,则认为锁被占用,继续自旋式地等待(并不放弃CPU).

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

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

也就是所谓的"自适应".

于此同时synchronized内部也会统计当前锁对象上,有多少线程参与竞争,如果整体CPU消耗大,则会转为重量级锁.

重量级锁

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

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

1.执行加锁操作,先进入内核态

2.在内核态判定当前锁是否已经被占用

3.如果该锁没有占用,则加锁成功,并切换回用户态.

4.如果该锁被占用,则加锁失败.此时线程进入锁的等待队列,挂起.等待被操作系统唤醒.

5.经历了一系列的沧海桑田,这个锁已经被线程释放了,操作系统也想起了这个挂起的线程,于是唤醒了这个线程,尝试重新获取到锁.

其它的优化操作

锁消除

也是synchronized中内置的优化策略,代码不需要加锁就会删除.

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

什么是"锁消除"

编译器优化的一种方式,编译器编译代码时,如发现该代码不需要加锁,就会把锁自动干掉.

有些应用程序的代码中,用到了synchronized,但其实没有在多线程环境下.(例如StringBuffer)

StringBuffer sb = new StringBuffer();
sb.append("a");
sb.append("b");
sb.append("c");

此时每个append的调用都会涉及到加锁和解锁.但如果只是在单线程中执行这个代码,那么这些加锁解锁是没必要的,白白浪费了一些资源开销.

锁粗化

一段逻辑中如果多次出现加锁解锁,编译器+JVM会自动进行锁的粗化.

即讲多个细粒度的锁,合并成一个粗粒度的锁.(代码越少越细,越多越粗).

锁的粒度:粗和细

通常情况下,其实是更偏好让锁的粒度更细一些,更有利于多个线程并发执行的.

但是实际上可能并没有其它线程来抢占这个锁.这种情况JVM就会自动把锁粗化,避免频繁申请释放锁.

举个例子理解锁粗化:

领导给下属交代工作两种方式:

方式1:

打电话,交代任务1,挂电话.

打电话,交代任务2,挂电话.

打电话,交代任务3,挂电话.

方式2:

打电话,交代任务1,交代任务2,交代任务3,挂电话.

显然方式2更为高效.

相关面试题

什么是偏向锁?

偏向锁并不是真的加锁,而只是在锁的对象头中记录一个标记(记录该锁所属的线程).如果没有其它线程参与竞争锁,那么就不会真正执行加锁操作,从而降低程序开销.一旦真的涉及到其它的线程竞争,再取消偏向锁的状态,进入轻量级锁的状态.

相关文章
|
29天前
|
Java
【多线程系列】你先说说synchronized的实现原理
面试官:听说你精通多线程,那我就考考你吧面试官:不用慌尽管说,错了也没关系😊。。。❤️。
18 0
【多线程系列】你先说说synchronized的实现原理
|
3月前
|
存储 监控 安全
Synchronized 实现原理
Synchronized 实现原理
42 0
|
2月前
|
存储 安全 Java
Synchronized锁工作原理
Synchronized锁工作原理
|
8月前
|
安全 Java 编译器
CAS 与 synchronized 原理
CAS 与 synchronized 原理
46 0
|
8月前
|
存储 安全 编译器
Synchronized底层原理
Synchronized底层原理
67 0
|
10月前
|
Java API 调度
synchronized 和 ReentrantLock 的实现原理是什么?它们有什么区别
synchronized 和 ReentrantLock 的实现原理是什么?它们有什么区别
57 0
|
10月前
|
存储 安全 Java
synchronized 的底层原理
synchronized 的底层是通过 Java 中的监视器锁(monitor)来实现的。每个 Java 对象都有一个与之对应的监视器锁,当一个线程获取了该对象的监视器锁,就可以执行 synchronized 代码块或方法。其他线程只能等待该线程释放锁,才能获取该对象的监视器锁并执行 synchronized 代码块或方法。
80 0
synchronized 的底层原理
|
11月前
|
Java 程序员 编译器
synchronized原理
synchronized原理
|
Java
synchronized原理剖析
synchronized原理剖析
83 0
|
安全 Java 对象存储
浅谈synchronized锁原理
保证线程安全的一个重要手段就是通过加锁的形式实现,今天盘点一下Java中锁的八股文
129 0