Synchronized 的理解与使用
package com.pengshi.ThreadSychronized;
import org.openjdk.jol.info.ClassLayout;
/**
* @description: synchronized 的理解
* synchronized 实现原理是对象
* 对象 的 组成 :1. 对象头 —— 类的属性
* 2. 实例变量 —— 类中的数据
* 3. 数据对齐 —— 为的是 以 8B 倍数对齐
*
* com.pengshi.ThreadSychronized.test object internals:
* OFF SZ TYPE DESCRIPTION VALUE
* 0 8 (object header: mark) 0x0000000000000005 (biasable; age: 0) // 对象头 64位计算机下 占位 8 B
* 8 4 (object header: class) 0x00067450 // 类的元数据信息,也就是类模板信息
* 12 1 boolean test.b true // 实例数据
* 13 3 (object alignment gap) // 对齐补充
* Instance size: 16 bytes
* Space losses: 0 bytes internal + 3 bytes external = 3 bytes total
* 这个对象头格式 是 公共格式 , Java对象 和 JVM 对象通用
*
*
*
* @projectName: Algorithm
* @see: com.pengshi.ThreadSychronized
* @author: pc
* @createTime: 2022/2/26 17:06
* @version: 1.0
*/
class test1 {
boolean b = true;
}
public class synchronizedTest {
public static void main(String[] args) {
test1 t = new test1();
test t1 = new test();
System.out.println(Integer.toHexString(t.hashCode()));
// 查看 对象头 信息 注意大小端存储的特性
System.out.println(ClassLayout.parseInstance(t).toPrintable());
System.out.println(ClassLayout.parseInstance(t1).toPrintable());
}
}
- 底层 Monitor 对象 是一种重量级锁
- _recursions重入锁概念
- monitor 对象
- owner 正在运行线程
- waitSet 调用阻塞线程的方法wait的集合
- _cxq 竞争的等待队列
- _EntryList
锁的获取
- 通过CAS 尝试 把 monitor 的owner 字段设置为当前线程
- 设置之前的owner指向当前线程,说明当前线程再次进入monitor,即重入锁,将 _recursions ++ ,记录重入次数
- 如果第一次进入,设置 _recursions = 1,owner为当前线程,并且返回
- 获取锁失败,等待锁的释放进行自旋,自旋过程中不断 请求获取锁的操作,同时包装成为node放入_cxq中
Synchronized 优化
无锁 ——> 偏向锁 ——> 轻量级锁 ——> 重量级锁
- 轻量级锁 就是 线程交替进行运行,没有竞争的条件
- 重量级锁就是上面monitor的实现
CAS 比较并且交换
- 其实就是通过比较 发现数据不同时候 ,进行交换 处理
- Atomic 的原理 就是 CAS
- CAS 主要是用在 JMM 模型中的 主存数据不一致的问题 多线程模型要进行 副本内存进行存储
- 所以 线程 实现的过程中 使用CAS 不断 对于主存中数据进行比较,调用,同步自己副本数据
- 在调用 副本数据 过程中 ,可能导致 线程干预,导致 副本数据 和 主存 数据不一致
- 所以 使用CAS 比较并且交换 进行设计 通过 不断获取主存数据 在 和 副本数据 比较,相同时候,在进行操作
偏向锁 (代码块中有锁的使用)
认为 只有 某一个线程 进行 锁的获取,适用于一个线程
-
线程第一次进入时候,把对象头设置成 线程id 、修改状态
-
一旦有两个线程 获取时候 ,就会撤销偏向锁
-
暂停拥有的偏向锁的线程,判断对象是否处于被锁定状态
Java 开始启动时候 会很慢 ,所以添加参数 加快启动时间XX:BiasedLockStartupDelay=0
关闭偏向锁 XX:UserBiasedLocking=false
轻量级锁
偏向锁 的升级 —— 轻量级锁
- 在线程栈帧 里面 创建一个
LockRecord
存储对象头 Mark Word,存储指针
自旋锁
就是竞争锁的 时候 ,轻量级锁要 变为 重量级锁,但是还要挣扎一下,所以就有自旋锁出现
锁消除
通过 即时性编译器 (JIT) 进行判断同步代码块的 逃逸分析 ,堆上的所有数据不会逃逸出去 被其他线程访问,不会进行加锁。
锁粗化
对于代码块 频繁加锁的代码,JVM 自己探测到 细小操作 可以加一次锁,进行锁的范围进行放大