深入了解Java线程锁(一)

news/2024/3/28 18:16:52/文章来源:https://blog.csdn.net/yuiezt/article/details/129171655

在上一篇《如何保证线程的原子性》中,我们谈到了锁(Synchronized),
这次我们就来深入探讨一下Java多线程中的锁。


互斥锁的本质是共享资源。
在这里插入图片描述

如上图所示,

  1. Thread1访问受保护资源,对其加锁,将受保护资源的锁加成粉红色。
  2. 这时Thread2来访问,发现锁对象是红色的,就阻塞等待。
  3. 当Thread1 释放锁后,锁恢复为绿色,表示资源可以使用。
  4. Thread2 可以正常访问资源。
    在这里插入图片描述
    上面例子中的粉色、绿色,其实就是锁对象的一些属性,这些属性存储在哪里呢?

接下来我们就来说一下 锁的存储
锁的存储,跟对象头有关系。
在hotspot 虚拟机中,对象头就是表示一个对象在内存中的布局。
在这里插入图片描述
比如我们new了一个lock对象,那么它在虚拟机中会构建一个对象布局。
我们从虚拟机源码中可以看出一些端倪。
instanceOop.hpp 文件中:

#ifndef SHARE_VM_OOPS_INSTANCEOOP_HPP
#define SHARE_VM_OOPS_INSTANCEOOP_HPP#include "oops/oop.hpp"// An instanceOop is an instance of a Java Class
// Evaluating "new HashTable()" will create an instanceOop.class instanceOopDesc : public oopDesc {public:// aligned header size.static int header_size() { return sizeof(instanceOopDesc)/HeapWordSize; }// If compressed, the offset of the fields of the instance may not be aligned.static int base_offset_in_bytes() {// offset computation code breaks if UseCompressedClassPointers// only is truereturn (UseCompressedOops && UseCompressedClassPointers) ?klass_gap_offset_in_bytes() :sizeof(instanceOopDesc);}static bool contains_field_offset(int offset, int nonstatic_field_size) {int base_in_bytes = base_offset_in_bytes();return (offset >= base_in_bytes &&(offset-base_in_bytes) < nonstatic_field_size * heapOopSize);}
};#endif // SHARE_VM_OOPS_INSTANCEOOP_HPP

每个Java的Class实例,都会对应一个这样的 instanceOop
来看一下它的父类 oopDesc

class oopDesc {friend class VMStructs;private:volatile markOop  _mark;union _metadata {Klass*      _klass;narrowKlass _compressed_klass;} _metadata;......
}

oopDesc 中的 markOop 就是我们上图中的对象头, _metadata 是元数据。
我们打开 markOop.hpp文件,可以看到对象头的每位所存储的内容的注释:

在这里插入图片描述
通过上述内容,可以知道,锁对象中有个空间来存储锁的状态。
当线程来临的时候,就可以通过锁状态来判断,是否可以进入到当前代码。

我们可以使用jol-core 这个工具来打印出来类的布局,加深理解。
代码pom.xml中加入:

   <dependency><groupId>org.openjdk.jol</groupId><artifactId>jol-core</artifactId><version>0.10</version></dependency>

编写一个类,输出一个无锁状态下的打印:

import org.openjdk.jol.info.ClassLayout;public class MyClassLayout {public static void main(String[] args) {MyClassLayout myClassLayout = new MyClassLayout();System.out.println(ClassLayout.parseInstance(myClassLayout).toPrintable());}
}

执行结果:

demo.MyClassLayout object internals:OFFSET  SIZE   TYPE DESCRIPTION                               VALUE0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)8     4        (object header)                           05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes totalProcess finished with exit code 0

对象头的每位所存储的内容可以通过下表来看:
在这里插入图片描述


再对照上述打印出的类布局:

      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)

注意 这里是小端模式存储的,所以是下面的顺序。

16进制: 0x 00 00 00 00 00 00 00 01
(64位) 2进制:00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001

对照上面表格,最后三位 0 01 就是表示无锁状态。


我们将代码中加入锁,再看下类布局:

public class MyClassLayout {public static void main(String[] args) {MyClassLayout myClassLayout = new MyClassLayout();synchronized (myClassLayout) {System.out.println(ClassLayout.parseInstance(myClassLayout).toPrintable());}}
}

执行结果:

demo.MyClassLayout object internals:OFFSET  SIZE   TYPE DESCRIPTION                               VALUE0     4        (object header)                           a8 f4 df e7 (10101000 11110100 11011111 11100111) (-404753240)4     4        (object header)                           5a 00 00 00 (01011010 00000000 00000000 00000000) (90)8     4        (object header)                           05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

上述结果中,最后三位是:000 表示轻量级锁。


我们多个线程访问一个资源时,我们判断是谁抢占来锁,一定要做出标记,来记录锁的持有者。
加锁一定会带来性能开销。所以为了优化,设计了多种性能开销不同的锁。
对于这些锁,各自有什么特性,我们后续再讲。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_73640.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【GO】k8s 管理系统项目16[前端部分–前端布局]

【GO】k8s 管理系统项目[前端部分–前端布局] 1. 前端布局 2. Layout 2.1 layout src/layout/Layout.vue <template><div class"common-layout"><el-container><el-side width"200">Aside</el-side><el-container>…

CAN总线开发一本全(3) - 微控制器集成的FlexCAN外设

CAN总线开发一本全&#xff08;3&#xff09; - 微控制器集成的FlexCAN外设 苏勇&#xff0c;2023年2月 文章目录CAN总线开发一本全&#xff08;3&#xff09; - 微控制器集成的FlexCAN外设引言硬件外设模块系统概要总线接口单元 - 寄存器清单数据结构 - 消息缓冲区MB初始化过…

React(一):初识React、类组件、jsx的基础语法

React&#xff08;一&#xff09;一、初识React1.简单介绍2.React的三个依赖3.Hello React案例二、类组件1.定义类组件并渲染2.绑定事件函数&#xff08;奇怪的this问题&#xff09;3.数组形式数据的展示&#xff08;电影案例&#xff09;4.计数器案例三、jsx语法详解1.jsx的书…

利用InceptionV3实现图像分类

最近在做一个机审的项目&#xff0c;初步希望实现图像的四分类&#xff0c;即&#xff1a;正常&#xff08;neutral&#xff09;、涉政&#xff08;political&#xff09;、涉黄&#xff08;porn&#xff09;、涉恐&#xff08;terrorism&#xff09;。有朋友给推荐了个github上…

机器学习笔记之近似推断(一)从深度学习角度认识推断

机器学习笔记之近似推断——从深度学习角度认识推断引言推断——基本介绍精确推断难的原因虽然能够表示&#xff0c;但计算代价太大无法直接表示引言 本节是一篇关于推断总结的博客&#xff0c;侧重点在于深度学习模型中的推断任务。 推断——基本介绍 推断(Inference\text{…

Python中实现将内容进行base64编码与解码

一、需求说明需要使用Python实现将内容转为base64编码&#xff0c;解码&#xff0c;方便后续的数据操作。二、base64简介Base64是一种二进制到文本的编码方式【是一种基于 64 个可打印字符来表示二进制数据的表示方法&#xff08;由于 2^664&#xff0c;所以每 6 个比特为一个单…

国产音质好的蓝牙耳机有哪些?国产音质最好的耳机排行

随着时间的推移&#xff0c;真无线蓝牙耳机逐渐占据耳机市场的份额&#xff0c;成为人们日常生活中必备的数码产品之一。蓝牙耳机品牌也多得数不胜数&#xff0c;哪些国产蓝牙耳机音质好&#xff1f;下面&#xff0c;我们从音质出来&#xff0c;来给大家介绍几款国产蓝牙耳机&a…

硬件系统工程师宝典(11)-----去耦电容布局“有讲究”

各位同学大家好&#xff0c;欢迎继续做客电子工程学习圈&#xff0c;今天我们继续来讲这本书&#xff0c;硬件系统工程师宝典。 上篇我们说到在电源完整性分析的目标就是要做到电源的干净、稳定和快速响应&#xff0c;以及针对不同噪声处理的实现方法。今天我们来看看去耦电容…

父传子与子传父步骤

父传子&#xff1a; 问题&#xff1a;父页面中引入子组件 把想要传给子页面的值用在子组件中用 &#xff1a;值“值” (用同一个值好区分)来绑定。 在子页面中用props接收 子组件不能改变父组件传过来的值。&#xff08;传多个页面的时候是&#xff0c;比如父传孙的时候我会…

【2023】华为OD机试真题Java-题目0221-AI处理器组合

AI处理器组合 题目描述 某公司研发了一款高性能AI处理器。每台物理设备具备8颗AI处理器,编号分别为0、1、2、3、4、5、6、7。编号0-3的处理器处于同一个链路中,编号4-7的处理器处于另外一个链路中,不通链路中的处理器不能通信,如下图所示。现给定服务器可用的处理器编号数…

STM32---备份寄存器BKP和 FLASH学习使用

BKP库函数 学习BKP&#xff0c;首先就是知道BKP每一个函数的作用然后如何使用即可 使用备份域的作用只需要操作上面的两个函数即可&#xff0c;其余的都是它的其他功能 BKP简介 备份寄存器是42个16位的寄存器&#xff0c;可用来存储84个字节的用户应用程序数据。他们处在备份…

如何高效管理自己的时间,可以从这几个方向着手

如果你是上班族&#xff0c;天选打工人&#xff0c;你的绝大多数时间都属于老板&#xff0c;能够自己支配的时间其实并不多&#xff0c;所以你可能察觉不到时间管理的重要性。但如果你是自由职业者或者创业者&#xff0c;想要做出点成绩&#xff0c;那你就需要做好时间管理&…

2. Dart 开发工具环境配置

很多编辑器都可以用来开发dart&#xff0c;所以大家可以选择自己喜欢的编辑器去进行开发。我还是比较喜欢vs code如果你不用vs code来开发dart的话&#xff0c;这篇文章可以直接跳过。如果想要在vs code里有dart的语法提示&#xff0c;我们需要安装相关的插件如图点开插件输入d…

预编译(回顾)

浏览器运行机制变量提升机制私有变量提升步骤全局对象 GO 和全局变量对象 VO的区别带var和不带var的区别系统输出顺序变量提升在条件判断下的处理防止函数的变量提升 浏览器运行机制 为了能够让代码执行&#xff0c;浏览器首先会形成一个执行环境栈 ECStack(Execution Contex…

TCP协议和TCP连接

TCP协议和TCP连接一、TCP协议的简介二、TCP连接的简介1、TCP连接的建立&#xff08;TCP三次握手&#xff09;2、TCP连接的断开&#xff08;TCP四次挥手&#xff09;一、TCP协议的简介 TCP&#xff08;Transmission Control Protocol&#xff09;传输控制协议是一种面向连接的、…

java 接口 详解

目录 一、概述 1.介绍 : 2.定义 : 二、特点 1.接口成员变量的特点 : 2.接口成员方法的特点 : 3.接口构造方法的特点 : 4.接口创建对象的特点 : 5.接口继承关系的特点 : 三、应用 : 1.情景 : 2.多态 : ①多态的传递性 : ②关于接口的多态参数和多态…

股票量化交易SQL特征工程入门

虽然现在各种量化教程和自助平台铺天盖地&#xff0c;但是对于新人来说入门最重要的事情就是挖掘特征。 对于传统的学习路径第一步是学习Python或者某一门编程语言&#xff0c;虽说Python入门容易上手快&#xff0c;但是要在实际应用中对股票数据进行分析&#xff0c;并挖掘有…

2023/2/24 图数据库Neo4j的理解与应用

1 什么是图数据库&#xff08;graph database&#xff09; 十大应用案例&#xff1a;https://go.neo4j.com/rs/710-RRC-335/images/Neo4j-Top-Use-Cases-ZH.pdf “大数据”每年都在增长&#xff0c;但如今的企业领导者不仅需要管理更大规模的数据&#xff0c;还迫切需要从现有…

8000+字,就说一个字Volatile

简介 volatile是Java提供的一种轻量级的同步机制。Java 语言包含两种内在的同步机制&#xff1a;同步块&#xff08;或方法&#xff09;和 volatile 变量&#xff0c;相比于synchronized&#xff08;synchronized通常称为重量级锁&#xff09;&#xff0c;volatile更轻量级&…

【大数据】记一次hadoop集群missing block问题排查和数据恢复

问题描述 集群环境总共有2个NN节点&#xff0c;3个JN节点&#xff0c;40个DN节点&#xff0c;基于hadoop-3.3.1的版本。集群采用的双副本&#xff0c;未使用ec纠删码。 问题如下&#xff1a; bin/hdfs fsck -list-corruptfileblocks / The list of corrupt files under path…