synchronized、ReentrantLock、volatile

发布时间:2022-06-21 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了synchronized、ReentrantLock、volatile脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

名词解释

  • synchronized 是Java中的关键字,是一种同步锁,可以修饰代码块,方法,静态的方法,类。synchronized(Object) 不能用String常量、Integer、 Long。
  • ReentrantLock 是一种同步锁,可以实现公平锁机制,获取锁和释放锁都需要手动操作。
  • volatile 是Java中的关键字,保障可见性,有序性,并不能保证原子性。
    • 可见性 当线程t1开始运行的时候,会把running值从内存中读到t1线程的工作区,在运行过程中直接使用这个copy,并不会每次都去读取堆内存,这样,当主线程修改running的值之后,t1线程感知不到,所以不会停止运行,使用volatile,将会强制所有线程都去堆内存中读取running的值。
    • 有序性 volatile关键字能禁止指令重排序,所以volatile能在一定程度上保证有序性。
    • 原子性 不可分割,最小执行单位。

synchronized和ReentrantLock区别

  • synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活; ReentrantLock也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。

  • synchronized可重入,因为加锁和解锁自动进行,不必担心最后是否释放锁; ReentrantLock也可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。

  • synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以响应中断。

  • ReentrantLock可以实现公平锁机制,synchronized不行。

synchronized和volatile区别

  • volatile保证线程的可见性,有序性,但是不能保证其原子性;synchronized保证线程的原子性、可见性,但是不能保证其有序性。

synchronized码解析

Java对象组成及Synchronized锁存放位置

对象是放在堆内存中的,对象大致可以分为三个部分,分别是对象头,实例变量和填充字节。

synchronized、ReentrantLock、volatile

  • 对象头,主要包括两部分1. Mark Word (标记字段),2.Klass Pointer(类型指针)。Klass Point 是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例(即指向方法区类的模版信息)。Mark Word用于存储对象自身的运行时数据。
  • 实例变量,存放类的属性数据信息,包括父类的属性信息,这部分内存按4字节对齐。
  • 填充字节,由于虚拟机要求对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐。

假如有如下的类,a=100这个信息就存储在实例变量中

public class test {
    int a = 100;
}

填充数据主要是为了方便内存管理,如你想要10字节的内存,但是会给你分配16字节的内存,多出来的字节就是填充数据

synchronized不论是修饰方法还是代码块,都是通过持有修饰对象的锁来实现同步,那么synchronized锁对象是存在哪里的呢?答案是存在锁对象的对象头Mark Word,来看一下Mark Word存储了哪些内容?

由于对象头的信息是与对象自身定义的数据没有关系的额外存储成本,因此考虑到JVM的空间效率,Mark Word 被设计成为一个非固定的数据结构,以便存储更多有效的数据,它会根据对象本身的状态复用自己的存储空间,也就是说,Mark Word会随着程序的运行发生变化,变化状态如下 (32位虚拟机):

synchronized、ReentrantLock、volatile

锁升级过程

在JDK1.6之前,synchronized都是重量级锁,1.6版本之后,进行了锁的升级;锁可以升级但不能降级,但是偏向锁状态可以被重置为无锁状态。

  • 无锁 没有线程运行时,此时处于无锁状态。

  • 偏向锁 当线程1访问代码块并获取锁对象时,会在java对象头和栈帧中记录偏向的锁的threadID,因为偏向锁不会主动释放锁,因此以后线程1再次获取锁的时候,需要比较当前线程的threadID和Java对象头中的threadID是否一致,如果一致(还是线程1获取锁对象),则无需使用CAS来加锁、解锁;如果不一致(其他线程,如线程2要竞争锁对象,而偏向锁不会主动释放因此还是存储的线程1的threadID),那么需要查看Java对象头中记录的线程1是否存活,如果没有存活,那么锁对象被重置为无锁状态,其它线程(线程2)可以竞争将其设置为偏向锁;如果存活,那么立刻查找该线程(线程1)的栈帧信息,如果还是需要继续持有这个锁对象,那么暂停当前线程1,撤销偏向锁,升级为轻量级锁,如果线程1 不再使用该锁对象,那么将锁对象状态设为无锁状态,重新偏向新的线程。

  • 自旋锁(轻量级锁)

  • 重量级锁 https://blog.csdn.net/zzti_erlie/article/details/103997713

脚本宝典总结

以上是脚本宝典为你收集整理的synchronized、ReentrantLock、volatile全部内容,希望文章能够帮你解决synchronized、ReentrantLock、volatile所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。