synchronized底层实现
标签: IT面试
upupor   93   0 2020-08-04 11:15 
最新一次编辑的原因:

Synchronized一般都是用在下面这几种场景:

  1. 修饰实例方法,对当前实例对象this加锁
  2. 修饰静态方法,对当前类的Class对象加锁
  3. 修饰代码块,指定一个加锁的对象,给对象加锁

其实就是锁方法、锁代码块和锁对象,那他们是怎么实现加锁的呢?

在这之前,我就先跟大家聊一下我们Java对象的构成

在 JVM 中,对象在内存中分为三块区域:

  • 对象头
    • Mark Word(标记字段):默认存储对象的HashCode,分代年龄和锁标志位信息。它会根据对象的状态复用自己的存储空间,也就是说在运行期间Mark Word里存储的数据会随着锁标志位的变化而变化。
    • Klass Point(类型指针):对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
  • 实例数据
    • 这部分主要是存放类的数据信息,父类的信息。
  • 对其填充
    • 由于虚拟机要求对象起始地址必须是8字节的整数倍,填充数据不是必须存在的,仅仅是为了字节对齐。

Tip:不知道大家有没有被问过一个空对象占多少个字节?就是8个字节,是因为对齐填充的关系哈,不到8个字节对其填充会帮我们自动补齐。

我们经常说到的,有序性、可见性、原子性,synchronized又是怎么做到的呢?

有序性

CPU会为了优化我们的代码,会对我们程序进行重排序。

as-if-serial

不管编译器和CPU如何重排序,必须保证在单线程情况下程序的结果是正确的,还有就是有数据依赖的也是不能重排序的。

就比如:

int a = 1;
int b = a;

这两段是怎么都不能重排序的,b的值依赖a的值,a如果不先赋值,那就为空了。

可见性

需要说到JMM(Java内存模型),JMM并不是实际存在的,而是一套规范,这个规范描述了很多java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这样的底层细节,Java内存模型是对共享数据的可见性、有序性、和原子性的规则和保障。

原子性

其实他保证原子性很简单,确保同一时间只有一个线程能拿到锁,能够进入代码块这就够了。

 

这几个是我们使用锁经常用到的特性,那synchronized他自己本身又具有哪些特性呢?

可重入性

synchronized锁对象的时候有个计数器,他会记录下线程获取锁的次数,在执行完对应的代码块之后,计数器就会-1,直到计数器清零,就释放锁了。

那可重入有什么好处呢?

可以避免一些死锁的情况,也可以让我们更好封装我们的代码。

不可中断性

不可中断就是指,一个线程获取锁之后,另外一个线程处于阻塞或者等待状态,前一个不释放,后一个也一直会阻塞或者等待,不可以被中断。

值得一提的是,Lock的tryLock方法是可以被中断的。


本作品系原创,采用《署名-非商业性使用-禁止演绎4.0 国际》许可协议.转载请说明出处
本文链接:https://www.upupor.com/u/20080411153055470592 复制分享

无评论内容,快来评论吧