【Java】并发编程-synchronized篇

锁机制篇——synchronized

synchronized是java关键字,相当于一把锁,用于解决线程安全问题

一、synchronized的使用

1、什么是锁

锁是一种资源,谁获取了锁,谁就拥有执行锁所管辖(锁住)范围内的代码的权限。

synchronized {  锁住的范围  }
  • 方法上修饰:锁住该方法内的所有代码
public synchronized void show(){
	锁管辖范围
}
  • 代码块上修饰:锁住代码块中的部分
public void show(){
	lalala
	synchronized(obj){
		锁管辖范围
	}
	lalalla
}

2、谁可以当锁

java是一种面向对象的编程语言,在java中,万事万物皆对象。所以,锁也不例外,必然是某个对象。由于对象可以分为两类:1)实例对象和2)类对象。因此,对应充当锁的对象也可分为两类:1)实例对象锁类对象锁

  • 实例对象锁:Object obj=new Object() obj可以是锁
  • 类对象锁:类名.class可以是锁

3、谁是锁

当synchronized关键字标注在代码块上时,后面小括号中会明确标注谁是锁(可以是任意对象、可以是类对象,也可以是实例对象;可以是该类也可以不是该类的类对象或实例对象),这个时候谁是锁毋庸置疑。但是当其标注在方法上时,并没有明确指明谁是锁。这种情况下,具体是谁在充当锁呢,小朋友你是否有很多问号???

  • 如果是静态方法,锁是该方法所在类的大class对象
  • 如果是非静态的方法,锁是调用该方法的实例对象本身this

4、使用注意事项

  • 只有线程在争抢同一把锁是时,才会成功阻塞其它线程
    • 类对象和该类的实例对象充当锁时,它们是两把锁,没有关系,谁也锁不住谁。充当锁的对象都是平等关系,没有谁包含谁的关系。
  • 它是可重入锁:可以重复获取
  • 它是非公平锁:并不是按照先来先到的原则获取锁资源

二、底层原理

1、字节码层面看synchronized

synchronized在字节码层面相当加了三个字节码指令:一个monitorenter和两个monitorexit。它们分别对应加锁和解锁在操作系统层面的一系列命令,本质上synchronized关键字代表的锁机制是OS层面提供的。

【问题】为什么会有两个monitorexit呢?
答:在执行同步代码块时,很有可能发生异常。jvm为了避免发生异常时导致锁资源无法正常释放,进而产生死锁问题,因此有两个释放锁的字节码指令。
在这里插入图片描述

2、对象在内存中的组织结构

在jvm运行时内存中,存储的对象并只有包含类中定义的成员变量,还包括对象本身的一些信息(https://blog.csdn.net/weixin_45880263/article/details/106157587)。
对象 = 对象头(对象信息) + 对象体(对象中的成员变量+补齐位)

对象头

  • mark word
    在这里插入图片描	述
  • Class word: 指向该实例对象大class对象的引用
    在这里插入图片描述

3、JVM中的锁以及每种锁的运行原理

1)轻量级锁
  • 获取锁的线程中会创建一个锁记录, 每个锁记录包含两个信息
    • 1)指向锁对象的引用
    • 2)锁记录自己的引用 | 00
  • 当重复获取锁时,cas操作会失败,不过由于获取锁的线程是同一个,这种失败无关紧要,它会在栈帧中增加锁记录的个数,但是其对应的锁记录的第二个信息存储为null

在这里插入图片描述

2)重量级锁
  • 当cas失败是由于不同线程获取锁导致的,轻量级锁会升级成重量级锁
    • os创建monitor对象,将锁对象的mark word指向monitor对象,monitor中标识锁拥有者的信息指向锁记录
    • 将争夺锁资源的线程排队到monitor对象的entrylist中

在这里插入图片描述

后面都是锁优化

3)自旋锁

重量级锁存在大量竞争的情况下,会降低线程执行效率,所以jvm通过优化锁机制——增加自旋锁来减少开销。

  • 发现当前是重量级锁情况,新来线程开始自旋,自旋过程中如果可以获取锁成功,就避免了线程转换为阻塞态的开销
  • 如果发现之前自旋可以成功,jvm会自动地增加自旋次数;如果发现自旋失败,会自动地减少自旋次数,甚至关闭自旋优化功能。
    • 自旋只适合多核cpu,单核情况下甚至会降低性能,因为自旋相当于不处理业务逻辑地空转,会占用cpu时间片
    • jdk6之后自旋变得智能(自适应),jdk7之后不可人为设置是否支持自旋
4)偏向锁

锁重入会不停执行cas操作,这个消耗也不小,我们希望对此进行优化,引入了偏向锁。

  • 首次获取锁,锁对象头mark word中不存储锁记录引用|00,而是存储os分配的线程id|101,这样重入时就不需要进行cas操作
  • 如果新线程争抢,偏向锁会升级为轻量级锁
  • 注意事项
    • 首次获取偏向锁时,偏向锁生效有延迟,可以通过参数-XX:BiasedLocklyStatusDelay=0取消延迟
    • 开启关闭偏向锁的参数-XX:+UseBiasedKocking/-XX:-UseBiasedKocking
5)批量重偏向

默认如果偏向锁更改次数超多20,则jvm认为我偏向错了,批量一次性全部重新偏向。

6)消除偏向锁

默认如果偏向锁更改次数超多40,则jvm认为我又偏向错了,竞争太激烈,决定取消偏向锁的支持。

7)锁消除

jvm发现你加了synchronized的代码其实不会发生争夺,就会通过JIT取消加锁

8)锁粗化

锁粒度太细,导致频繁的加锁和解锁过程,JIT可优化让锁范围扩大

*)jvm锁机制流程

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值