一.synchronized的基本语法
synchronized有三种方式来加锁,分别是:
1.修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
2.修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁
3.修饰代码块,指定加锁对象,对给定的对象加锁,进入同步代码块前要获得给定对象的锁
不同的修饰类型,代表锁的控制粒度
二.synchronized 锁是如何存储的
1.要实现多线程的互斥特性,那么这把锁需要哪些因素?
1)锁需要有一个东西来表示,比如获得锁是什么状态,无锁状态是什么状态
2)这个状态需要对多个线程共享
2.synchronized 锁是如何存储的
synchronized(lock)是基于lock 这个对象的生命周期来控制锁粒度的,所以可以想到锁的存储和这个lock对象有关系,于是我们以对象在jvm内存中是如何存储作为切入点,去看看对象里面有什么特性能够实现锁
3.对象在内存中的布局
在 Hotspot 虚拟机( Hotspot 虚拟机是jvm虚拟机的一种实现,jvm是一种标准)中,对象在内存中的存储布局,可以分为三个区域:对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)
当我们在 Java 代码中,使用 new 创建一个对象实例的时候,(hotspot 虚拟机)JVM 层面实际上会创建一个instanceOopDesc 对象。Hotspot 虚拟机采用 OOP-Klass 模型来描述 Java 对象实例,OOP(Ordinary Object Point)指的是普通对象指针,Klass 用来描述对象实例的具体类型。Hotspot 采用instanceOopDesc 和 arrayOopDesc 来 描述对象头,
instanceOopDesc是继承自oopDesc,在普通实例对象中,oopDesc的定义包含两个成员,分别是_mark和_metadata,_mark 表示对象标记、属于 markOop 类型,也就是接下来
要讲的 Mark World,它记录了对象和锁有关的信息_metadata 表示类元信息,类元信息存储的是对象指向它的类元数据(Klass)的首地址,其中 Klass 表示普通指针、_compressed_klass 表示压缩类指针
4.MarkWord
在 Hotspot 中,markOop 的定义在 markOop.hpp 文件中,Mark word 记录了对象和锁有关的信息,当某个对象被synchronized 关键字当成同步锁时,那么围绕这个锁的一系列操作都和 Mark word 有关系
5.为什么任何对象都可以实现锁
- 首先,Java 中的每个对象都派生自 Object 类,而每个Java Object 在 JVM 内部都有一个 native 的 C++对象oop/oopDesc 进行对应。
- 线程在获取锁的时候,实际上就是获得一个监视器对象(monitor) ,monitor 可以认为是一个同步对象,所有的Java 对象是天生携带 monitor。在 hotspot 源码的markOop.hpp 文件中,可以看到下面这段代码。
多个线程访问同步代码块时,相当于去争抢对象监视器修改对象中的锁标识,上面的代码中ObjectMonitor这个对象和线程争抢锁的逻辑有密切的关系
三.synchronized 锁的升级
在分析 markword 时,提到了偏向锁、轻量级锁、重量级锁。在分析这几种锁的区别时,我们先来思考一个问题使用锁能够实现数据的安全性,但是会带来性能的下降。不使用锁能够基于线程并行提升程序性能,但是却不能保证线程安全性。这两者之间似乎是没有办法达到既能满足性能也能满足安全性的要求。hotspot 虚拟机的作者经过调查发现,大部分情况下,加锁的代码不仅仅不存在多线程竞争,而且总是由同一个线程多次获得。所以基于这样一个概率,是的 synchronized 在JDK1.6 之后做了一些优化,为了减少获得锁和释放锁带来
的性能开销,引入了偏向锁、轻量级锁的概念。因此大家会发现在 synchronized 中,锁存在四种状态分别是:无锁、偏向锁、轻量级锁、重量级锁; 锁的状态根据竞争激烈的程度从低到高不断升级
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/2333.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...