Synchronized的使用及原理理解

1,synchronized的使用

synchronized 有三种方式来加锁,分别是

  1. 修饰实例方法,作用于当前实例加锁,进入同步代码前
    要获得当前实例的锁
  2. 静态方法,作用于当前类对象加锁,进入同步代码前要
    获得当前类对象的锁
  3. 修饰代码块,指定加锁对象,对给定对象加锁,进入同
    步代码库前要获得给定对象的锁。
    不同的修饰类型,代表锁的控制粒度。

2,锁是如何存储的

对象在内存中的布局:(对象是怎么存储的)
在 Hotspot 虚拟机中,对象在内存中的存储布局,可以分为三个区域:对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)。

实例变量:存放类的属性数据信息,包括父类的属性信息,如果是数组的实例部分还包括数组的长度,这部分内存按4字节对齐。

填充数据:由于虚拟机要求对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐。

对象头:主要结构是由Mark Word 和 Class Metadata Address 组成。
Mark Word:用于存储对象自身的运行时数据, 如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等。
class Pointer: 对象指向它的类的元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。(数组,对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是从数组的元数据中无法确定数组的大小。 )

32位的HotSpot虚拟机对象头存储结构:
在这里插入图片描述

对象头是synchronized的锁对象的基础,JVM 基于进入和退出Monitor对象来实现方法同步和代码块同步,monitorenter指令是在编译后插入到同步代码块的开始位置,monitorexit是插入到方法结束处和异常处。

monitor是由ObjectMonitor实现的,ObjectMonitor中有两个队列,_WaitSet 和 _EntryList,用来保存ObjectWaiter对象列表( 每个等待锁的线程都会被封装成ObjectWaiter对象),_owner指向持有ObjectMonitor对象的线程,当多个线程同时访问一段同步代码时,首先会进入 _EntryList 集合,当线程获取到对象的monitor 后进入 _Owner 区域并把monitor中的owner变量设置为当前线程同时monitor中的计数器count加1,若线程调用 wait() 方法,将释放当前持有的monitor,owner变量恢复为null,count自减1,同时该线程进入 WaitSe t集合中等待被唤醒。若当前线程执行完毕也将释放monitor(锁)并复位变量的值,以便其他线程进入获取monitor(锁)。

所以,monitor对象存在于每个Java对象的对象头中(存储的指针的指向),synchronized锁便是通过这种方式获取锁的,也是为什么Java中任意对象可以作为锁的原因,同时也是notify/notifyAll/wait等方法存在于顶级对象Object中的原因

3,锁的升级

在 synchronized 中,锁存在四种状态
分别是:无锁、偏向锁、轻量级锁、重量级锁; 锁的状态根据竞争激烈的程度从低到高不断升级。

4,Synchronized

线程在获取锁的时候,实际上就是获得一个监视器对象(monitor) ,monitor 可以认为是一个同步对象,所有的Java 对象是天生携带 monitor。 线程在获取锁的时候,实际上就是获得一个监视器对象(monitor) ,monitor 可以认为是一个同步对象,所有的Java 对象是天生携带monitor。

扫描二维码关注公众号,回复: 12908058 查看本文章

猜你喜欢

转载自blog.csdn.net/weixin_43978695/article/details/109061619
今日推荐