关键字介绍
# java多线程关键字解析
# synchronized 关键字
# synchronized关键字介绍
Synchronized关键字是Java中用于实现线程同步的重要特性之一。它可以应用于方法或代码块,用于确保在多线程环境下对共享资源的安全访问。
对象锁
包括方法锁(默认锁对象为this,当前实例对象)和同步代码块锁(自己指定锁对象)
- 同步代码块形式
- 修饰属性方法
类锁
指synchronize修饰静态的方法或指定锁对象为Class对象
- 修饰静态代码块
- 锁对象是class对象
# Synchronized原理分析
# 加锁释放锁原理
从字节码角度看,被加了synchronized关键字的代码,当线程进入临界区时,会遇到三种情况
- 锁对象上,锁计数器为0,此时执行
monitorenter
命令,锁计数器+1 - 锁对象上,锁计数器大于1,获取锁失败,此时其他线程持有对象锁,线程进入阻塞队列
- 锁对象上,锁计数器大于1,获取锁成功,此时执行
monitorenter
命令,锁计数器+1
线程离开临界区时,会执行Monitorexit
命令,锁计数器+1,知道减为0为止,释放锁。
# 可重入原理:加锁次数计数器
即在同一锁程中,每个对象拥有一个monitor计数器,当线程获取该对象锁后,monitor计数器就会加一,释放锁后就会将monitor计数器减一,线程不需要再次获取同一把锁
# 保证可见性的原理:happens-before规则
如果A 先于 B执行,那么A的结果必然会被B看到
# jvm对锁的优化
JDK1.6之前,Synchronized会导致,多线程下,线程间从用户态到内核态的频繁切换,会比较耗费性能,所以1.6之后提出了大量优化。
# 自旋锁
当发生多线程抢占同一个资源时,线程不再是挂起并从用户态切换到内核态来执行lock操作,而是进行自旋,仍然占用cpu,等到锁释放,再去抢占锁。适合任务执行时间短的情况
# 锁粗化
如果 代码中多处对同一个锁对象加锁和释放锁操作,jvm会在字节码层面优化成,把锁加在整个代码块,减少锁操作
# 锁消除
JVM会判断再一段程序中的同步明显不会逃逸出去从而被其他线程访问到,那JVM就把它们当作栈上数据对待,认为这些数据是线程独有的,不需要加同步。此时就会进行锁消除。
# 轻量级锁
属于乐观锁,适用于无锁竞争环境下,利用cas加锁,允许线程在自己的栈帧中记录对象的锁信息。
# 偏向锁
偏向锁的目的是在无竞争的情况下快速地获取锁,从而提高性能。 也属于乐观锁,适用于无锁竞争环境下,比轻量级锁还乐观,无需cas操作。 当一个线程获取了对象的锁之后,如果没有其他线程争用该锁,那么该线程在以后的执行过程中可以偏向于自己,无需重新获取锁,从而避免了不必要的同步操作。
# 锁升级过程(JDK.16之后)
前置知识:对象头中的markword(32字节)的最后两个字节用来表示锁的状态
无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁 (无法退化)
- 无锁状态 会被标志无锁状态,jvm启动4秒后会变为偏向锁状态
- 偏向锁,最后两个字节会标志为偏向锁,且还会有块内存区域标志着线程id的信息,下次线程过来时,直接将锁交出,当有竞争时,偏向锁会升级为轻量级锁
- 轻量级锁,采用的是cas操作,cas成功的线程获取到锁,其他的线程会自旋,等待锁释放,轻量级锁实现的过程是:线程的虚拟机栈中会创建一个lock record内存块用来保存对象头markword副本,对象头会记录lock record的地址,如果自旋线程数量超过一个,那么就会升级为重量级锁
- 重量级锁 jvm层面,使用monitor enter 和 monitor exist命令加锁,其他线程挂起
# 与lock比较
# volatile关键字
# 介绍
是Java中的关键字,用于修饰变量。它的主要作用是保证被修饰的变量在多线程环境中的可见性和禁止指令重排序。
# 如何保证可见性
缓存行,缓存一致性协议,但是会有伪共享问题,
伪共享解决方案:
- 对象大小填充满缓存行
- 加上 sun.misc.Contended注解
# 如何禁止指令重排
插入内存屏障