JUC —— Synchronized 的理解与使用

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 对象 是一种重量级锁

image-20220226182052458

  • _recursions重入锁概念
  • monitor 对象
  • owner 正在运行线程
  • waitSet 调用阻塞线程的方法wait的集合
  • _cxq 竞争的等待队列
  • _EntryList

锁的获取

  1. 通过CAS 尝试 把 monitor 的owner 字段设置为当前线程
  2. 设置之前的owner指向当前线程,说明当前线程再次进入monitor,即重入锁,将 _recursions ++ ,记录重入次数
  3. 如果第一次进入,设置 _recursions = 1,owner为当前线程,并且返回
  4. 获取锁失败,等待锁的释放进行自旋,自旋过程中不断 请求获取锁的操作,同时包装成为node放入_cxq中

Synchronized 优化

无锁 ——> 偏向锁 ——> 轻量级锁 ——> 重量级锁

  • 轻量级锁 就是 线程交替进行运行,没有竞争的条件
  • 重量级锁就是上面monitor的实现

CAS 比较并且交换

  • 其实就是通过比较 发现数据不同时候 ,进行交换 处理
  • Atomic 的原理 就是 CAS
  • CAS 主要是用在 JMM 模型中的 主存数据不一致的问题 多线程模型要进行 副本内存进行存储
  • 所以 线程 实现的过程中 使用CAS 不断 对于主存中数据进行比较,调用,同步自己副本数据
  • 在调用 副本数据 过程中 ,可能导致 线程干预,导致 副本数据 和 主存 数据不一致
  • 所以 使用CAS 比较并且交换 进行设计 通过 不断获取主存数据 在 和 副本数据 比较,相同时候,在进行操作

偏向锁 (代码块中有锁的使用)

认为 只有 某一个线程 进行 锁的获取,适用于一个线程

  • 线程第一次进入时候,把对象头设置成 线程id 、修改状态

  • 一旦有两个线程 获取时候 ,就会撤销偏向锁

  • 暂停拥有的偏向锁的线程,判断对象是否处于被锁定状态

Java 开始启动时候 会很慢 ,所以添加参数 加快启动时间XX:BiasedLockStartupDelay=0

关闭偏向锁 XX:UserBiasedLocking=false

轻量级锁

偏向锁 的升级 —— 轻量级锁

  • 在线程栈帧 里面 创建一个 LockRecord存储对象头 Mark Word,存储指针

自旋锁

就是竞争锁的 时候 ,轻量级锁要 变为 重量级锁,但是还要挣扎一下,所以就有自旋锁出现

  • 避免 线程切换的消耗,

  • 也就是线程 不阻塞 ,不断循环尝试获取锁

  • 默认自旋数量是 10 次

    自适应自旋锁

    自适应 修改 自选数量

锁消除

通过 即时性编译器 (JIT) 进行判断同步代码块的 逃逸分析 ,堆上的所有数据不会逃逸出去 被其他线程访问,不会进行加锁。

锁粗化

对于代码块 频繁加锁的代码,JVM 自己探测到 细小操作 可以加一次锁,进行锁的范围进行放大

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pengshi12138

加油加油

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值