目录
一、synchronized基本特点
二、synchronized加锁工作过程
2.1 无锁
2.2 偏向锁
2.3 轻量级锁
2.4 重量级锁
三、synchronized其他优化操作
3.1 锁消除
3.2 锁粗化
一、synchronized基本特点
- 开始是乐观锁,如果锁冲突频繁就会转换成悲观锁
- 开始是轻量级锁,如果锁被持有的时间变长就会升级成重量级锁
- 轻量级锁是通过自旋锁来实现的,重量级锁基于系统的互斥锁来实现的
- 是不公平锁(这里的不公平指的是不按照顺序执行线程,谁抢到算谁的)
- 是可重入锁(内部会通过计数器来记录加锁次数)
- 不是读写锁
二、synchronized加锁工作过程
由于synchronized具有多种特性,所以在加锁的过程中也是有不同的状态。Jvm将synchronized锁分为:无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁 这4种状态,会根据不同的场景依次进行升级。
2.1 无锁
无锁的解释很简单,线程可能加锁了,但是该线程还能没有被执行,此时synchronized就还处于无锁的状态。也就是说synchronized不会一开始就加锁,这也是效率提升的原因。
2.2 偏向锁
偏向锁不是真的加锁,只是对这个这个锁进行一个“偏向锁标记”,记录当前锁属于哪个线程。当后续有线程进行锁竞争的时候再升级为轻量级锁。
这里的这种做法就类似于单例模式中的懒汉模式,能不加锁就不加锁,尽量避免不必要的开销。但是还是会做好标记,在遇到锁竞争的时候方便加锁。
2.3 轻量级锁
随着其他线程进入竞争,偏向锁的状态解除,进入轻量级锁状态(自适应的自旋锁)。其他线程可能没有直接放弃,而是持续访问查看上个线程是否释放锁,如果释放就能第一时间拿到锁。但是这种情况下也有缺点,如果是上个线程持有的时间比较久,那么这样就会持续的消耗cpu资源。
2.4 重量级锁
如果锁竞争进一步激烈,自旋不能快速的获取到锁,那么synchronized就会膨胀为重量级锁。这里的重量级锁是指内核提供的mutex。
- 执行加锁操作, 先进入内核态.
- 在内核态判定当前锁是否已经被占用
- 如果该锁没有占用, 则加锁成功, 并切换回用户态.
- 如果该锁被占用, 则加锁失败. 此时线程进入锁的等待队列, 挂起. 等待被操作系统唤醒.
- 经历了一系列的沧海桑田, 这个锁被其他线程释放了, 操作系统也想起了这个挂起的线程, 于是唤醒这个线程, 尝试重新获取锁.
三、synchronized其他优化操作
3.1 锁消除
这里的锁消除指的是当代码中加了synchronized锁但是并没有多线程,这里的编译器+Jvm就会直接将锁进行消除。但是并不是所有的场景编译器+Jvm都会进行这种优化,所以在编写代码的过程中还是需要自己编写的规范,不要过度依靠外物。
3.2 锁粗化
首先要了解一下这里的“粗”是指的什么,这里“粗”指的是synchronized中执行代码的多少,如果一段代码中多次出现加锁解锁,编译器+Jvm就会自动执行锁的粗化。
举个例子理解锁粗化:
滑稽老哥当了领导, 给下属交代工作任务:
方式一:
打电话, 交代任务1, 挂电话.
打电话, 交代任务2, 挂电话.
打电话, 交代任务3, 挂电话.
方式二:
打电话, 交代任务1, 任务2, 任务3, 挂电话.
显然, 方式二是更高效的方案.