【并发编程】synchornized原理

       📝个人主页:五敷有你      
 🔥
系列专栏:并发编程
⛺️稳重求进,晒太阳


 

目录

Monitor概念

Java对象头

普通对象

数组对象

Monitor(锁)

Monitor结构如下:

注意:

原理之synchornized

        轻量级锁

锁膨胀(重量级锁)

自旋优化

偏向锁

示例

 轻量级锁与偏向锁加锁的对比

偏向状态

一个对象创建时:

撤销-调用对象hashCode

撤销-其他线程使用对象

批量重定向

批量撤销

锁消除


Monitor概念

Java对象头

以32位机的机器为例

普通对象

数组对象

其中Mark Word结构为

Monitor(锁)

Monitor被翻译为监视器或管程

每个Java对象都可以关联一个Monitor对象,如果使用synchronized给对象上锁(重量级)之后,该对象头的Mark Word中就被设置指向Monitor对象的指针。

Monitor结构如下:

  • 刚开始Monitor中Owner为null
  • 当Thread-2执行synchronized(obj) 就会将Monitor 的所有者Owner设置为Thread-2,Monitor中只能有一个Owner.
  • 在Thread-2上锁的过程中,如果Thread-3,Thread-4也来执行synchronized(obj) 就会进入EntryList BLOCKED阻塞队列
  • Thread-2执行完同步代码块的内容,然后唤醒EntryList 中等待的线程来竞争锁,竞争的锁是非公平的。
  • 图中WaitSet中的Thread-0,Thread-1 是之前获得过锁,但条件不满足进入WAITING 状态的线程。后面会将wait-notify

注意:

  • synchronized 必须是进入同一个对象的monitor才有上述的效果
  • 不加synchronized 的对象不会关联监视器,不遵从以上规则

原理之synchornized

        轻量级锁

        轻量级锁的使用场景:如果一个对象虽然有多线程访问,但多线程访问的时间是错开的(也就是没有竞争)。那么就可以使用轻量级锁优化

        轻量级锁对使用者是透明的,即语法仍然是synchronized

        假设有两个同步块,利用同一个对象加锁。

static final Object obj=new Object();
public static void method1(){synchornized(obj){//同步块 Amethod2();}
}
public static void method2(){synchronized(obj){//同步块b    }
}
  • 创建锁对象,每个线程的栈帧会包含一个锁记录的结构,内部可以存储锁定对象的Mark Word

  • 让锁记录中的Object reference指向锁对象,并尝试使用CAS替换Object的Mark Word,将Mark Word的值存入锁记录

  • 如果CAS替换成功,对象头中存储了锁记录地址和状态,表示由该线程给对象加锁,这时图示如下:

  • 如果cas失败:
    • 如果是其他线程已经持有了该Object的轻量级锁,这时候表面有竞争,进入锁膨胀过程。
    • 如果是自己执行了synchronized 锁重入,那么再添加一条Lock Record 作为重入的计数

  • 当退出synchronized 代码块(解锁时) 如果有取值为null 的记录,表示有重入,这时重置锁记录,表示重入计数-1

  • 当退出synchronized 代码块(解锁时)锁记录的值不为null ,这时使用cas将Mark Word的值恢复给对象头

成功:解锁成功

失败: 说明轻量级锁进行了锁膨胀或已经升级为重量级锁。进入重量级锁解锁流程

锁膨胀(重量级锁)

如果在尝试加轻量级锁的过程中,CAS操作无法成功,这时一种情况就是有其他线程为此对象加上了轻量级锁(有竞争),这时需要进行锁膨胀,将轻量级锁变为重量级锁。

当Thread -1进行轻量级加锁时,Thread-0已经对该对象加了轻量级锁

这时候Thread-1加锁失败,进入锁膨胀流程。

  • 即为Object对象申请Monitor锁,让Object指向重量级锁地址
  • 然后自己进入Monitor的EntryList BLOCKED

  • 当Thread-0 退出同步代码块时,使用CAS将MarkWord 的值恢复给对象头,失败,这时候会进入重量级解锁流程,按照Monitor地址找到Monitor对象,设置Owner为null, 唤醒EntryList 中的BLOCKED
自旋优化

自选在多核CPU下才有意义

重量级锁竞争的时候,还可以使用自旋来进行优化,如果当前线程自选成功(即这时候锁线程已经退出了同步块,释放了锁)),

这时当前线程就可以避免阻塞。

偏向锁

        轻量级锁在没有竞争时(就自己这个线程),每次重入都需要进行CAS操作。

        Java6 引入了偏向锁来进一步优化,只有第一次使用CAS将线程ID设置到对象的Mark Word头,之后发现这个线程ID是自己的就表示没有竞争,不需要重新CAS,以后只要不发生竞争,这个对象就归线程所有。

示例
static final Object obj=new Object();
public static void m1(){synchronized(obj){//同步代码块Am2();    }
}
public static void m2(){synchronized(obj){//同步代码块Bm3();    }
}
public static void m3(){synchronized(obj){//同步块C    }
}
 轻量级锁与偏向锁加锁的对比

偏向状态

回忆一下对象头格式

一个对象创建时:

        如果开启了偏向锁,那么对象创建后,markword值为0x05即最后三位为101(表示可以偏向的状态),这时它的thread epoch age都为0

        偏向锁是默认是延迟的,不会在程序启动后立刻生效。如果想要避免延迟,可以加VM参数:xx:BiasedLockingstartupDalay=0来禁止延迟

        如果没有开启偏向锁,那么对象创建后,markword值为0x01,即后三位为001,这时它的hashcode 、age都为0,第一次用到hashcode时才会赋值。

(调用了hashcode会让偏向状态禁用,让他变为不可偏向的对象)

撤销-调用对象hashCode

调用了对象的hashCode,但偏向锁的对象MarkWord中存储的是线程id,如果调用hashCode会导致偏向锁被撤销

  • 轻量级锁会在锁记录中记录hashCode
  • 重量级锁会在Monitor中纪录hashCode

在调用hashCode后使用偏向锁,记得去掉-xx:UseBiassedLocking

输出

撤销-其他线程使用对象

        当有其他线程使用偏向锁时,会偏向锁升级为轻量级锁

批量重定向

        如果对象虽然被多个线程访问,但没有竞争,这时候偏向了线程T1的对象仍有机会重新偏向T2,重偏向会重置对象的ThreadID,

        当撤销偏向锁阈值超过20次后,jvm会认为,我是不是偏向错了,于是会在这些对象加锁时重新偏向加锁线程。

详解:

    我们知道,当我们使用synchronized关键字的时候,一个对象a只被一个对象访问的时候,对对象加的锁偏向锁,如果之后出现第二个线程访问a的时候(这里只考虑线程交替执行的情况,不存在竞争),不管线程1是已死亡还是运行状态,此时锁都会升级为轻量锁,并且锁升级过程不可逆。

    但是如果有很多对象,这些对象同属于一个类(假设是类A)被线程1访问并加偏向锁,之后线上2来访问这些对象(不考虑竞争情况),在通过CAS操作把这些锁升级为轻量锁,会是一个很耗时的操作。

JVM对此作了优化:

    当对象数量超过某个阈值时(默认20, jvm启动时加参数-XX:+PrintFlagsFinal可以打印这个阈值 ),Java会对超过的对象作批量重偏向线程2,此时前20个对象是轻量锁,后面的对象都是偏向锁,且偏向线程2。

批量撤销

当撤销偏向锁阈值超过40次后,jvm会这样觉得,我们确实偏向错了,根本就不该偏向,于是整个类的所有对象都会变为不可偏向的,新建的对象也是不可偏向的

锁消除

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

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

相关文章

跟杰哥一起学Flutter (一、开发初体验)

引言 学习Flutter的动机 Flutter出了好些年头了,最早可以追溯到 2015.6 的Google I/O大会,会上首次公开介绍了Flutter,不过直到 2017.5 才正式发布首个 Alpha版本,于 2018.12 发布 1.0版本。而后Flutter为了尽快推出产品&#xf…

Git 基本命令与操作流

记录 Git 中的基本命令和创建仓库、提交文件、删除文件等方面的操作 Git 基本命令 git status:查看状态 nothing to commit, working directory clean:所有已跟踪文件在上次提交后都未被更改过,或者说当前目录下没有出现任何处于未跟踪状态…

百度搜索Push个性化:新的突破

作者 | 通用搜索产品研发组 导读 本文简单介绍了百度搜索Push个性化的发展过程,揭示了面临的困境和挑战:如何筛选优质物料、如何对用户精准推荐等。我们实施了一系列策略方法进行突破,提出核心的解决思路和切实可行的落地方案。提升了搜索DAU…

postman案例

一、表单接口 基本正向 有效反向 无效反向 JSON接口 基本正向 有效反向 无效反向 文件上传接口 token 获取token值 一: 二: Bearer 获取的token的值,至于鉴权方式要根据swagger接口文档要求

uniapp踩坑之项目:canvas第一次保存是空白图片

在ctx.draw()回调生成图片,参考canvasToTempFilePath接口文档 // data imgFilePath: null,// 缓存二维码图片canvas路径//js // 首先在draw()里进行本地存储 ...... ctx.draw(false, () >{uni.canvasToTempFilePath({ // 把画布转化成临时…

Zookeeper简介

系列文章目录 Zookeeper安装教程 目录 一、Zookeeper简介 二、Zookeeper的数据结构 三、CPA理论 四、BASE 理论 五、ZooKeeper的特性 前言 这是我的学习笔记,以便后面翻阅。 一、Zookeeper简介 ZooKeeper是一个分布式的、开放源码的分布式应用程序协调服务&a…

轻量化/高效扩散模型文献综述

🎀个人主页: https://zhangxiaoshu.blog.csdn.net 📢欢迎大家:关注🔍点赞👍评论📝收藏⭐️,如有错误敬请指正! 💕未来很长,值得我们全力奔赴更美好的生活&…

适合进阶学习的 机器学习 开源项目(可快速下载)

目录 开源项目合集[>> 开源的机器学习平台:mlflow/mlflow](https://gitcode.com/mlflow/mlflow)[>> 机器学习路线图:mrdbourke/machine-learning-roadmap](https://gitcode.com/mrdbourke/machine-learning-roadmap)[>> 机器学习理论和…

5. UE5 RPG使用GAS技能系统

之前也介绍过GAS的使用: UE 5 GAS Gameplay Ability System UE 5 GAS 在项目中处理AttributeSet相关 UE 5 GAS 在项目中通过数据初始化 基础的讲解这里不再诉说,有兴趣的可以翻我之前的博客。 接下来,在RPG游戏中实现GAS系统的使用。 GAS系统…

16.鸿蒙HarmonyOS App(JAVA)滑块组件Slider与评级组件Rating

16.鸿蒙HarmonyOS App(JAVA)滑块组件Slider与评级组件Rating ability_main.xml <?xml version"1.0" encoding"utf-8"?> <DirectionalLayoutxmlns:ohos"http://schemas.huawei.com/res/ohos"ohos:height"match_parent"oh…

redis数据安全(三)数据持久化 AOF

接上一篇RDB&#xff0c;本篇看下Redis数据持久化的第二种方式AOF。 目录 一、AOF原理 1、写入机制&#xff1a; 2、缓冲机制&#xff1a; 3、重写机制 &#xff1a; 4、运行流程 二、AOF文件配置 1、开启AOF&#xff1a; 2、自动触发AOF重写 3、重写规则&#xff1…

unity面试题

一&#xff1a;什么是协同程序&#xff1f; 在主线程运行的同时开启另一段逻辑处理&#xff0c;来协助当前程序的执行&#xff0c;协程很像多线程&#xff0c;但是不是多线程&#xff0c;Unity的协程实在每帧结束之后去检测yield的条件是否满足。 二&#xff1a;Unity3d中的碰…

vue基于Spring Boot共享单车租赁报修信息系统

共享单车信息系统分为二个部分&#xff0c;即管理员和用户。该系统是根据用户的实际需求开发的&#xff0c;贴近生活。从管理员处获得的指定账号和密码可用于进入系统和使用相关的系统应用程序。管理员拥有最大的权限&#xff0c;其次是用户。管理员一般负责整个系统的运行维护…

redis cluster搭建

准备服务器和端口号 IP 地址端口号路径192.168.0.2016001/app/redis-5.0.14/redis-6001192.168.0.2016002/app/redis-5.0.14/redis-6002192.168.0.2026001/app/redis-5.0.14/redis-6001192.168.0.2036002/app/redis-5.0.14/redis-6002192.168.0.2036001/app/redis-5.0.14/redi…

查看神经网络中间层特征矩阵及卷积核参数

可视化feature maps以及kernel weights&#xff0c;使用alexnet模型进行演示。 1. 查看中间层特征矩阵 alexnet模型&#xff0c;修改了向前传播 import torch from torch import nn from torch.nn import functional as F# 对花图像数据进行分类 class AlexNet(nn.Module):d…

Spring Boot整合MyBatis-Plus

引言 在现代软件开发中&#xff0c;我们经常需要处理大量的数据。为了有效地管理这些数据&#xff0c;我们需要使用一些强大的框架。其中&#xff0c;Spring Boot和MyBatis-Plus是两个非常流行的框架。Spring Boot是一个基于Spring的开源Java框架&#xff0c;可以用于创建独立…

[一]ffmpeg音视频解码

[一]ffmpeg音视频解码 一.编译ffmpeg1.安装vmware虚拟机2.vmware虚拟机安装linux操作系统3.安装ftp和fshell软件4.在Ubuntu&#xff08;Linux&#xff09;中编译Android平台的FFmpeg&#xff08; arm和x86 &#xff09;5.解压FFmpeg6.Android编译脚本&#xff08;1&#xff09;…

vue+elementui实现12个日历平铺,初始化工作日,并且可点击

<template><div class"app-container"><el-form :model"queryParams" ref"queryForm" size"small" :inline"true"><el-form-item label"年份" prop"holidayYear"><el-date-…

can数据记录仪自带软件LKMaster——自动化测试篇

LKMaster上位机软件是由南京来可电子发布的CAN&CANFD综合测试分析软件&#xff0c;支持报文收发、数据分析、协议解析、历史回放、文件格式转换、参数配置、记录文件管理、脚本编辑、自动化测试等强大的功能。支持J1939、CANOPEN、J1939BMS、自定义解析&#xff0c;支持曲线…

redis原理(四)redis命令

目录 一、字符串命令&#xff1a; 二、列表命令&#xff1a; 三、集合命令&#xff1a; 四、散列命令&#xff1a; 五、有序集合命令&#xff1a; 六、redis发布与订阅命令&#xff1a; 七、事务命令 八、其他命令 1、排序&#xff1a;SORT 2、键的过期时间&#xff…