JVM - 高效并发

news2024/10/1 17:16:07

目录

Java内存模型和内存间的交互操作

Java内存模型

内存间的交互操作

内存间交互操作的规则

volatile特性

多线程中的可见性

volatile

指令重排原理和规则

指令重排

指令重排的基本规则

多线程中的有序性

线程安全处理

锁优化

锁优化之自旋锁与自适应自旋

锁优化之锁消除

锁优化之锁粗化

锁优化之轻量级锁

锁优化之偏向锁

JVM中获取锁的步骤

同步代码的基本规则


  • Java内存模型和内存间的交互操作

  • Java内存模型

  • JCP定义了一种Java内存模型,以前是在JVM规范中,后来独立出来成为JSR-133(Java内存模型和线程规范修订)
  • 内存模型:
    • 在特定的操作协议下,对特定的内存或高速缓存进行读写访问的过程抽象
  • Java内存模型主要关注JVM中把变量值存储到内存和从内存中取出变量值这样的底层细节

  • 所有变量(共享的)都存储在主内存中,每个线程都有自己的工作内存
  • 工作内存中保存该线程使用到的变量的主内存副本拷贝
  • 线程对变量的所有操作(读、写)都应该在工作内存中完成
  • 不同线程不能相互访问工作内存,交互数据要通过主内存
  • 内存间的交互操作

  • Java内存模型规定了一些操作来实现内存间交互,JVM会保证它们是原子的
  • lock:锁定,把变量标识为线程独占,作用于主内存变量
  • unlock:解锁,把锁定的变量释放,别的线程才能使用,作用于主内存变量
  • read:读取,把变量值从主内存读取到工作内存
  • load:载入,把read读取到的值放入工作内存的变量副本中
  • use:使用,把工作内存中一个变量的值传递给执行引擎
  • assign:赋值,把从执行引擎接收到的值赋给工作内存里面的变量
  • store:存储,把工作内存中一个变量的值传递到主内存中
  • write:写入,把store进来的数据存放进主内存的变量中

  • 内存间交互操作的规则

  • 不允许read和load、store和write操作之一单独出现,以上两个操作必须按顺序执行,但不保证连续执行
  • 也就是说,read与load之间、store与write之间是可插入其它指令的
  • 不允许一个线程丢弃它的最近的assign操作,即变量在工作内存中改变了之后必须把该变化同步回主内存
  • 不允许一个线程无原因地(没有发生过任何assign操作)把数据从线程的工作内存同步回主内存中
  • 一个新的变量只能从主内存中“诞生”,不允许在工作内存中直接使用一个未被初始化的变量
  • 也就是对一个变量实施use和store操作之前,必须先执行过了assign和load操作
  • 一个变量在同一个时刻只允许一条线程对其执行lock操作,但lock操作可以被同一个条线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁
  • 如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行load或assign操作初始化变量的值
  • 如果一个变量没有被lock操作锁定,则不允许对它执行unlock操作,也不能unlock一个被其他线程锁定的变量
  • 对一个变量执行unlock操作之前,必须先把此变量同步回主内存(执行store和write操作)
  • volatile特性

  • 多线程中的可见性

  • 可见性:就是一个线程修改了变量,其他线程可以知道
  • 保证可见性的常见方法:volatile、synchronized、final(一旦初始化完成,其他线程就可见)
  • volatile

  • volatile基本上是JVM提供的最轻量级的同步机制,用volatile修饰的变量,对所有的线程可见,即对volatile变量所做的写操作能立即反映到其它线程中
  • 用volatile修饰的变量,在多线程环境下仍然是不安全的
  • volatile修饰的变量,是禁止指令重排优化的
  • 适合使用volatile的场景:
    • 运算结果不依赖变量的当前值
    • 或者能确保只有一个线程修改变量的值
  • 指令重排原理和规则

  • 指令重排

  • 指的是JVM为了优化,在条件允许的情况下,对指令进行一定的重新排列,直接运行当前能够立即执行的后续指令,避开获取下一条指令所需数据造成的等待
  • 线程内串行语义,不考虑多线程间的语义
  • 不是所有的指令都能重排,比如:
    • 写后读a = 1;b = a;写一个变量之后,再读这个位置
    • 写后写a = 1;a = 2;写一个变量之后,再写这个变量
    • 读后写a = b;b = 1;读一个变量之后,再写这个变量
  • 以上语句不可重排,但是a = 1;b = 2;是可以重排的
  • 指令重排的基本规则

  • 程序顺序原则:
    • 一个线程内保证语义的串行性
  • volatile规则:
    • volatile变量的写,先发生于读
  • 锁规则:
    • 解锁(unlock)必然发生在随后的加锁(lock)前
  • 传递性:
    • A先于B,B先于C那么A必然先于C
  • 线程的start方法先于它的每一个动作
  • 线程的所有操作先于线程的终结(Thread.join())
  • 线程的中断(interrupt())先于被中断线程的代码
  • 对象的构造函数执行结束先于finalize()方法
  • 多线程中的有序性

  • 在本线程内,操作都是有序的
  • 在线程外观察,操作都是无序的,因为存在指令重排或主内存同步延时
  • 线程安全处理

  • 不可变是线程安全的
  • 互斥同步(阻塞同步):synchronized、java.util.concurrent.ReentrantLock
  • 目前这两个方法性能已经差不多了,建议优先选用synchronized
  • ReentrantLock增加了如下特性:
  • 等待可中断:
    • 当持有锁的线程长时间不释放锁,正在等待的线程可以选择放弃等待
  • 公平锁:
    • 多个线程等待同一个锁时,须严格按照申请锁的时间顺序来获得锁
  • 锁绑定多个条件:
    • 一个ReentrantLock对象可以绑定多个condition对象,而synchronized是针对一个条件的,如果要多个,就得有多个锁
  • 非阻塞同步:
    • 是一种基于冲突检查的乐观锁的策略,通常是先操作,如果没有冲突,操作就成功了,有冲突再采取其它方式进行补偿处理
  • 无同步方案:
    • 其实就是在多线程中,方法并不涉及共享数据,自然也就无需同步了
  • 锁优化

  • 锁优化之自旋锁与自适应自旋

  • 自旋:
    • 如果线程可以很快获得锁,那么可以不在OS层挂起线程,而是让线程做几个忙循环,这就是自旋
  • 自适应自旋:
    • 自旋的时间不再固定,而是由前一次在同一个锁上的自旋时间和锁的拥有者状态来决定
  • 如果锁被占用时间很短,自旋成功,那么能节省线程挂起、以及切换时间,从而提升系统性能
  • 如果锁被占用时间很长,自旋失败,会白白耗费处理器资源,降低系统性能
  • 锁优化之锁消除

  • 在编译代码的时候,检测到根本不存在共享数据竞争,自然也就无需同步加锁了
  • 通过-XX:+EliminateLocks来开启
  • 同时要使用-XX:+DoEscapeAnalysis开启逃逸分析
  • 所谓逃逸分析:
    • (1)如果一个方法中定义的一个对象,可能被外部方法引用,称为方法逃逸
    • (2)如果对象可能被其它外部线程访问,称为线程逃逸,比如赋值给类变量或者可以在其它线程中访问的实例变量
  • 锁优化之锁粗化

  • 通常都要求同步块要小,但一系列连续的操作导致对一个对象反复的加锁和解锁,这会导致不必要的性能损耗
  • 这种情况建议把锁同步的范围加大到整个操作序列
  • 锁优化之轻量级锁

  • 轻量级是相对于传统锁机制而言,本意是没有多线程竞争的情况下,减少传统锁机制使用OS实现互斥所产生的性能损耗
  • 其实现原理很简单,就是类似乐观锁的方式
  • 如果轻量级锁失败,表示存在竞争,升级为重量级锁,导致性能下降
  • 锁优化之偏向锁

  • 偏向锁是在无竞争情况下,直接把整个同步消除了,连乐观锁都不用,从而提高性能
  • 所谓的偏向,就是偏心,即锁会偏向于当前已经占有锁的线程
  • 只要没有竞争,获得偏向锁的线程,在将来进入同步块,也不需要做同步
  • 当有其它线程请求相同的锁时,偏向模式结束
  • 如果程序中大多数锁总是被多个线程访问的时候,也就是竞争比较激烈,偏向锁反而会降低性能
  • 使用-XX:-UseBiasedLocking来禁用偏向锁,默认开启
  • JVM中获取锁的步骤

  • 会先尝试偏向锁;然后尝试轻量级锁
  • 再然后尝试自旋锁
  • 最后尝试普通锁,使用OS互斥量在操作系统层挂起
  • 同步代码的基本规则

  • 尽量减少锁持有的时间
  • 尽量减小锁的粒度

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

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

相关文章

jvisualvm工具使用

jdk自带的工具jvisualvm,可以分析java内存使用情况,jvm相关的信息。 1、设置jvm启动参数 设置jvm参数**-Xms20m -Xmx20m -XX:PrintGCDetails** 最小和最大堆内存,打印gc详情 2、测试代码 TestScheduleClassGc package com.core.schedule;…

LeetCode 82. 删除排序链表中的重复元素 II

原题链接 难度:middle\color{orange}{middle}middle 题目描述 给定一个已排序的链表的头 headheadhead , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。 示例 1: 输入:head [1,2,3,…

ASML逆袭史:人、资金、技术,缺一不可

前言 近年来,由于众所周知的原因,荷兰ASML(阿斯麦)公司的先进半导体制造设备——光刻机,进入普通大众视野,成为人们茶余饭后谈论的焦点话题之一。 1月底,“美日荷三方谈判达成协议,可…

Selenium自动化测试Python二:WebDriver基础

欢迎阅读WebDriver基础讲义。本篇讲义将会重点介绍Selenium WebDriver的环境搭建和基本使用方法。 WebDriver环境搭建 Selenium WebDriver 又称为 Selenium2。 Selenium 1 WebDriver Selenium 2 WebDriver是主流Web应用自动化测试框架,具有清晰面向对象 API&…

SAP ABAP 输出结果带有空格

方法一: 字段内容前增加空格,需使用全角空格,使用半角空格时,ALV显示无效,空格无法显示, 全角与半角的切换方法:shift空格切换, 如下的标记部分,要想通过ALV显示空格&…

mfc140u.dll丢失的解决方法,mfc140u.dll文件修复

mfc140u.dll丢失的解决方法,其实要解决这个问题一点都不难,我们主要知道是什么原因造成的,那么就可以轻松的解决。 一.mfc140u.dll是什么 "MFC140u.dll"是一个Windows动态链接库文件,它是Microsoft Visual C 2015运行…

TortoiseSVN的使用

基本概念 版本库 SVN保持数据的地方,所有的文件都保存在这个库中,Tortoise访问的就是远程服务器上的Subversion版本库。 工作拷贝 就是工作副本,可将版本库的文件拷贝到本地中,可以任意修改, 不会影响版本库。在你…

责任链模式(Chain of Responsibility Pattern)

意图:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。 主要解决:职责链上的处理者负责处理请求,客户只…

常用调试golang的bug以及性能问题的实践方法

文章目录如何分析程序运行时间和CPU利用率情况1.shell内置time指令/usr/bin/time指令如何分析golang程序的内存使用情况?1.内存占用情况查看如何分析golang程序的CPU性能情况1.性能分析注意事项2.CPU性能分析A.Web界面查看B.使用pprof工具查看如何分析程序运行时间和…

PHP(12)文件上传

PHP(12)文件上传一、文件上传原理二、表单写法三、预定义变量 $_FILES四、移动临时文件五、多文件上传1. 同名表单2. 不同名表单六、多文件处理1. 同名文件2. 不同名文件七、封装文件上传函数一、文件上传原理 文件从客户机上传至服务器指定目录。 步骤…

Redhat7.6升级openssh(超详细)

一、准备工作 从官网下载新版的openssh-7.9p1.tar.gz 准备rhel-server-7.6-x86_64-dvd.iso用于使用yum安装依赖 二、具体升级步骤 1.查看系统版本 [rootredhat ~]# cat /etc/redhat-release Red Hat Enterprise Linux Server release 7.6 (Maipo) 2.查看openssh现有版本 …

金三银四面试必看,自动化测试如何解决日志问题

前言 前几天在员群里,有同学问了一个自动化测试实践中遇到的问题: 持续集成的自动化用例很多,测试环境日志level为debug,日志量大概40G/每天,定位问题时日志查询很慢,该怎么解决? 这个问题可…

pytorch基础入门教程

pytorch基础入门教程 Pytorch一小时入门教程 前言 机器学习的门槛并没有想象中那么高,我会陆续把我在学习过程中看过的一些文章和写过的代码以博客的形式分享给大家,和大家一起交流,这个是本系列的第一篇,pytoch入门教程&#x…

软件测试2-测试必须有策略和测试有哪些最高原则

什么是软件测试测试是为发现错误而执行程序的过程。软件测试一个破坏性的过程,甚至是一个施虐的过程,也就是第一天说的“找茬”游戏。 当一个输入框让我输入手机号码时,我偏不,我要输入非手机号码,甚至不填。 当界面提…

定时任务使用总结

定时任务表达式生成工具网站:https://cron.qqe2.com/定时任务选型:xxl-job 官方文档:https://www.xuxueli.com/xxl-job/安装定时任务调度中心 xxl-job-admin第一步、先导入xxl-job的数据库:地址:https://gitee.com/xux…

2.2 多区域集成IS-IS

2.2.2 实验二:多区域集成IS-IS 1. 实验目的  实现IS-IS协议DIS优先级修改 实现IS-IS协议网络类型修改 实现IS-IS协议外部路由引入 实现IS-IS接口cost修改 实现IS-IS路由渗透配置2. 实验拓扑 配置多区域集成IS-IS如图2-5所示: …

Java 万年历、周六日计算、节假日导出

目录 通过 Java 的基本语法来实现万年历 Java 获取一年中所有的周六和周日 Java 节假日导入导出 通过 Java 的基本语法来实现万年历 在 Java 的时间计算方面还有很多好用的工具类,Java 常用的工具类封装框架链接如下:HUTool 框架官网 package com.ta…

企业降本增效的催化剂:敏捷迭代

伴随着开源技术的大爆发,新一代的软件技术如雨后春笋般层出不穷。每家企业在硬件及软件开发上都有许多开源技术可选,目的还是在于提高效率,降低开发成本。 本篇文章,带大家了解下促进企业降本增效的重要理念:敏捷迭代…

前端错误/性能监控(vue)

配置目录结构 错误监听:可以提前发现前端的错误,并且找到对应的位置进行修改。因为等等环境因素可能导致不同的问题,这些问题难以发现,影响用户体验。 性能监听:可以及时发现问题,比如下载的js文件、image时…

Stream流源码分析及技巧(含大量案例)

Stream流源码分析及技巧(含大量案例) 目录 Stream流源码分析及技巧(含大量案例) 更新说明 简介(这部分摘了部分官方文档) 特性 Stream接口关系图 Stream流接口方法 Stream流之间的转换 与Stream流相…