ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java内置锁的核心原理(一)

2022-01-25 20:58:44  阅读:164  来源: 互联网

标签:内置 Word 对象 Mark Java 线程 JVM 原理


引言:线程安全问题

什么是线程安全问题?当多个线程并发的访问一个Java对象时,无论系统如何调度这些线程,这个对象都能表现出一致的、正确的行为,那么我们就说对这个对象的操作是线程安全的。反之,对这个线程的操作不是线程安全的,发生了线程安全问题。

本文将回答如下几个问题:synchronize是如何保证线程安全的? synchronize加锁到底是怎么加的? 锁信息放在Object对象的什么位置? 如何查看锁升级的具体过程?等等。

关于synchronize的使用场景、synchronize与ReentryLock有什么区别将在下一期介绍Lock的博客中给出。

Java内置锁的核心原理

Java内置锁是一种互斥(独占)锁。当线程A占有一个对象的锁,线程B也想要尝试获取这个对象的内置锁时,线程B会等待或阻塞,直到线程A执行完成后后释放锁(抛出异常也会释放),如果线程A不释放,线程B将会永远等待下去。

Java中的每个对象都已可用作内置锁。线程进入同步代码块时会自动获取改锁,在退出代码块时会自动释放该锁。

1. synchronized关键字

synchronize关键字的使用方式有三种:

1.修饰一个普通方法

public synchronized void add1() {
   val ++;
}

此时仅有一个线程可进入这个对象的这个同步方法。这时候锁是加在这个对象上的,即这个对象的this属性。

2.修饰代码块

public void add3() {
   synchronized (this) {
      val ++;
   }
}

与上面的代码稍有不同,synchronize修饰的是一段代码,因为很多情况下并不需要将整个方法全部锁住,仅锁住部分代码即可保证线程安全的执行。

还有一点不同的是我们既可以使用synchronize包裹住this对象,也可以包裹住其他的对象,实现更为灵活的锁操作。

3.修饰一个静态方法

public static synchronized void add2() {
   val ++;
}

Java的对象可以分为两大类,一类是Object对象,分配在JVM的堆中,另一类是Class对象,存放在方法区(Java1.8 的HotSpot实现中称为元空间),JVM中一个Class文件只会有一个Class类,而由Class实例化出的Object对象会有很多个。synchronize修饰静态方法时便是将锁加在了Class对象中。

小结:以上三种加锁的方法各异,但本质类似,都是锁住了一个Java对象从而实现一次只有一个线程可以访问同步代码块。好,了解了原理,那我们就来看看他的实现,synchronize具体是实现如何锁住一个对象的?

2. Java对象结构与内置锁

在介绍内置锁之前,有必要先和大家介绍下Java对象的结构。

image-20211114163922614

一个Java对象可以分为三个部分:

1)对象头

对象头又包含三个字段:第一个是Mark Word,用来存储对象的GC信息,锁信息,hashcode值等。第二个是Class Pointer,存放的是类指针,虚拟机通过这个类指针这个对象是哪个类的实例。第三个是Array Length,是一个可选字段,当此对象为数组时才会存在,用于记录数组长度的数据。

2)对象体

这部分包含对象的实例变量,包含父类的属性,这部分按照4字节对齐。

3)对齐字节

也叫填充区,其作用是保证这个对象占用的内存字节数为8的整数倍,因为对象头是8位的,所以仅需保证对象体也是8的倍数即可,当对象的实例变量数据不为8的倍数时,便需要填充来保证8字节的对齐。

Mark Word、Class Pointer、Array Length等字段的长度都与JVM的位数有关。Mark Word的长度为JVM的一个Word(字)大小,也就是说32位JVM的Mark Word为32位,64位JVM的Mark Word为64位。Class Pointer(类对象指针)字段的长度也为JVM的一个Word(字)大小,即32位JVM的Mark Word为32位,64位JVM的Mark Word为64位。所以,在32位JVM虚拟机中,Mark Word和Class Pointer这两部分都是32位的;在64位JVM虚拟机中,Mark Word和ClassPointer这两部分都是64位的。

不同锁状态下32位Mark Word的结构信息

image-2021111417134723864位的Mark Word与32位的Mark Word结构相似,

不同锁状态下64位Mark Work的结构信息

image-20211114171406229

对象加内置锁及锁的升级过程

无锁 --> 偏向锁:

当一个线程进入同步代码块时,发现此对象没有线程占用,那么这个对象就使用CAS(null, threadID)null是期望的值,threadID是将要写入的值,将自己的线程ID写入对象的Mark Word中,如果写入成功,即对象中没有threadID,则对象由原来的无锁状态变为偏向锁状态,lock不变化,为01,将偏向锁的标志位biased发生变化,由0变为1。

偏向锁 --> 轻量级锁:

当一个线程进入同步代码块时,使用CAS(null, threadID)将自己的线程ID写入对象的Mark Word中,如果写入失败,则说明此时线程已经被占用,则撤销对象的偏向锁定状态,升级为轻量级锁。对象Mark Word中的lock位由01变为00;

轻量级锁 --> 重量级锁:

轻量级锁的本意是为了减少多线程进入操作系统底层的互斥锁(Mutex Lock)的概率,并不是要替代操作系统互斥锁。所以,在争用激烈的场景下,轻量级锁会膨胀为基于操作系统内核互斥锁实现的重量级锁。轻量级锁升级为重量级锁的条件较为复杂,我们下一讲在详细探究。

标签:内置,Word,对象,Mark,Java,线程,JVM,原理
来源: https://blog.csdn.net/Mr_Zhang____/article/details/122691969

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有