多线程与高并发编程进阶(二)

2023-10-26

前言:
前文多线程与高并发入门中,已经介绍了多线程编程的目的以及实际应用中可能会遇到的问题,本文接着叙述关于多线程并发机制的底层原理–volatile以及synchronized;
一般来说,Java代码从编写到最后的执行会经历以下的过程:Java源代码(.java文件)–<即时编译器>–Java字节码(.class文件)–<类加载器加载>–汇编指令(CPU执行);Java中的并发机制依赖于JVM以及CPU指令,这些具体的实现原理将在下面进行分析;

在Java的并发机制中,比较典型也比较常见的两个关键字:volatile以及synchronized;volatile保证了共享变量在多线程中的可见性,对于可见性,可以简单地理解为当一个线程修改过volatile关键字修饰的共享变量之后,其他的线程再次访问该共享变量的值时,访问的是之前线程修改后的最新值;而对于synchronized关键字来说,在JDK1.6之前,它是比较重(阻塞以及上下文切换),很多人称其为重量级锁;

对于多个线程访问同一个共享变量,如果想要保证共享变量的准确一致的更新,一般采用的是使用排它锁(互斥锁)来保证对共享变量操作的线程安全;但是,使用volatile关键字来修饰共享变量,在某些情况下,比synchronized关键字更加方便,更加轻量,因为volatile关键字修饰的字段可以保证该字段的内存可见性;


volatile的实现原理
通过对volatile变量写操作对应的汇编指令分析可以发现,比普通变量的写操作会多出两行汇编指令,其中有一条指令带有lock前缀,查看IA-32架构软件开发者手册可以发现,带有lock前缀的指令在多核处理器下会做两件事情:

  1. 将当前处理器的缓存行中的数据写回系统内存(缓存锁定)
  2. 缓存行写回系统内存的同时会将其他处理器中缓存了对应内存地址的数据无效(利用缓存一致性协议–MESI控制协议)

**注:**对于普通的共享变量来说,何时将更新的值从缓存中写回系统内存是不确定的,并且即使写回系统内存,但是不会更新其他线程中对应的缓存值,所以会出现数据不一致的问题,而volatile修饰的共享变量,会在对其进行写操作时,通过lock前缀指令将缓存中的数据写回系统内存中,并且通过缓存一致性协议,可以将其他线程中对应的共享变量的值置为无效,使得其他线程必须从系统内存中再次读取最新的值进行后续的操作;

IA-32以及Intel 64处理器能够嗅探其他处理器访问系统内存和自己的内部缓存,处理器通过嗅探技术保证自己的内部缓存、系统内存以及其他处理器的内部缓存中的数据在总线上保持一致;
比如,一个处理器通过嗅探检测到其他处理器打算写内存地址,并且该地址处于S状态(共享状态),那么正在嗅探的处理器就会将自己对应的缓存行置为无效,在下次访问相同内存地址时,就会强制执行缓存行填充,从系统内存中读取最新的数据;


synchronized的实现原理:

  1. Java中每一个对象都可以作为锁,都与一个monitor与之关联
  2. Java中synchronized关键字的三种使用:
    ①普通同步方法:锁对应当前实例对象
    ②静态同步方法:锁对应当前类的Class对象
    ③同步方法块:锁对应显式指定的对象
  3. 锁的实现以及相关的信息
    ①锁的实现:JVM基于进入和退出Monitor对象来实现方法同步和代码块同步,两者的实现细节有所不同,同步代码块是通过monitorenter和monitorexist指令实现,同步代码块编译之后,会在在代码块的开始位置插入monitorenter指令,代码块中方法结束处和异常处插入monitorexist指令,JVM保证monitorenter和monitorexist指令必须成对出现;
    ②锁的相关信息:在Java对象头中的Mark Word中,会存放对象的HashCode、分代年龄以及锁标记位,在运行期间,Mark Word中存储的数据会随着锁标志位的变化而变化,具体如下图所示:
    这里写图片描述
  4. synchronized的优化——偏向锁
    **锁的四种状态:**无锁、偏向锁、轻量级锁以及重量级锁(锁的级别递增),并且锁的级别只能升不能降;

偏向锁:
**引入偏向锁的原因:**大多数情况下,锁都是有同一个线程获取的,为了降低由于不必要的锁的获取和释放导致的代价,引入偏向锁;
**偏向锁的实现原理:**当一个线程访问同步块并获取锁时,会在对象头和栈帧中存储锁偏向的线程ID,之后线程再进入和退出同步代码块时就不需要使用CAS操作来加锁和解锁,只需要检测对象头中的Mark Word中是否存储指向当前线程的偏向锁,如果是,则直接进入同步块,否则,需要测试Mark Word中偏向锁的标志是否设置为1,如果设置为1,则尝试使用CAS将对象头的偏向锁指向当前线程,否则,使用CAS竞争锁;
**偏向锁的撤销:**等到竞争出现时,才会释放锁,即当其他线程尝试竞争偏向锁时,持有偏向锁的线程才回释放锁;
**偏向锁的关闭:**可以使用JVM参数关闭偏向锁开启的延迟:-XX:BiasedLockingStartupDelay=0;使用JVM参数关闭偏向锁:-XX:UseBiasedLocking=false,程序进入轻量级状态;


轻量级锁:
**轻量级锁的加锁:**线程执行同步块之前,JVM会现在当前线程中的栈帧中创建用于存储锁记录的空间,并将对象中的Mark Word复制到锁记录中,接着线程尝试使用CAS将对象头中的Mark Word替换为指向锁记录的指针,替换成功,当前线程获得锁,否则,表示其他线程竞争锁,当前线程尝试使用自旋获取锁;
**轻量级锁的解锁:**解锁时,会使用CAS操作将原来的Mark Word替换回对象头,成功表明没有竞争发生,否则,锁膨胀为重量级锁;

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

多线程与高并发编程进阶(二) 的相关文章

  • 多个线程访问共享对象和数据的方式

    一 如果每个线程执行的代码相同 可以使用同一个Runnable 对象 这个Runnable对象中有那个共享的数据 例如买票系统就可以这样做 代码如下 package com thread class ShareDataTest1 publi
  • interview5-多线程篇

    一 线程的基础知识 1 线程与进程 程序由指令和数据组成 但这些指令要运行 数据要读写 就必须将指令加载至 CPU 数据加载至内存 在指令运行过程中还需要用到磁盘 网络等设备 进程就是用来加载指令 管理内存 管理 IO 的 进程 当一个程序
  • 等待和通知机制(wait和notify)

    1 等待和通知机制的实现 wait 方法 wait 是 Object 类的方法 它的作用是使当前执行wait方法的线程进行等待 该方法将当前线程置入 预执行队列 中 并在 wait 所在的代码行处停止执行 直到接到通知或者被中断才能继续执行
  • Java学习笔记-多线程实现方式

    Java学习笔记 多线程实现方式 注意 线程开启不一定立即执行 由CPU调度执行 1 继承Thread类 自定义线程类继承Thread类 重写run 方法 编写线程执行体 创建线程对象 调用start 方法启动线程 public class
  • 线程中捕获异常

    总结 正常线程抛出异常时 在外部是捕捉不到的 当此类异常跑抛出时 线程就会终结 而对于主线程和其他线程完全不受影响 且完全感知不到某个线程抛出的异常 也是说完全无法catch到这个异常 解决方案 为线程添加未捕获异常处理器 Uncaught
  • synchronized关键字(一)

    一 线程安全和不安全 非线程安全 在多个线程对同一个对象的实例变量进行并发访问时会出现值被更改 值不同步的情况 线程安全 获得的实例变量的值是经过同步处理的 按照顺序执行 不会出现脏读情况 举个例子 5个销售员 卖同一堆货物 每个销售员在卖
  • 【JAVA多线程11】线程基本方法-线程等待(wait)/线程唤醒(notify)

    1 wait notify notifyAll 方法是Object的本地final方法 无法被重写 2 wait 使当前线程阻塞 前提是 必须先获得锁 一般配合synchronized 关键字使用 即 一般在synchronized 同步代
  • JAVA高并发---收藏的好文章(持续更新)

    JAVA高并发 AQS详解 转载 学习前因 本来对多线程略懂 最近忽然看到了CountDownLatch 的用法 忽然想简单看看它的原理 了解一下它阻塞线程的方法 我只知道阻塞线程的lock 和wait notifyAll 才发现原来还有L
  • CopyOnWriteArrayList部分源码分析

    CopyOnWriteArrayList部分源码分析 我们都知道ArrayList是基于数组实现的可动态扩容的集合 但是他实际上也是线程不安全的 而在JUC java util concurrent 下有个线程安全的数组集合 就是CopyO
  • 深入研究java.lang.ThreadLocal类

    深入研究java lang ThreadLocal类 一 概述 ThreadLocal是什么呢 其实ThreadLocal并非是一个线程的本地实现版本 它并不是一个Thread 而是threadlocalvariable 线程局部变量 也许
  • Java中如何捕获其他线程抛出的异常

    如Java中另一个线程抛出的异常 可以使用公共静态接口Thread UncaughtExceptionHandler完成 Thread UncaughtExceptionHandler是当线程因未捕获的异常而突然终止时调用的处理程序接口 当
  • Java 并发工具包 java.util.concurrent 用户指南

    http blog csdn net defonds article details 44021605 comments 译序 本指南根据 Jakob Jenkov 最新博客翻译 请随时关注博客更新 http tutorials jenko
  • synchronized方法和代码块

    1 同步 由于多线程并发存在数据不安全问题 为了保证数据的安全性需要一些特殊的手段来维持 数据不安全主要是针对修改来说的 如果一个数据只能读不能修改几乎不会产生什么安全问题 只有修改数据的时候容易产生一些差错导致多线程并发造成数据不安全 从
  • Java线程中 suspend() 和 resume() 、wait() 和 notify()、park和unpark

    suspend 和 resume 方法 两个方法配套使用 suspend 使得线程进入阻塞状态 并且不会自动恢复 必须其对应的 resume 被调用 才能使得线程重新进入可执行状态 但suspend 方法阻塞时都不会释放占用的锁 很容易引起
  • Java并发编程:并发容器之CopyOnWriteArrayList(转载)

    http www cnblogs com dolphin0520 p 3938914 html 原文链接 http ifeve com java copy on write Copy On Write简称COW 是一种用于程序设计中的优化策
  • 【多线程】线程安全、锁的同步和异步

    一 基本概念 线程安全 当多个线程访问某一个类 对象或方法 时 这个类始终都能表现出正确的行为 那么这个类 对象或方法 就是线程安全的 非线程安全 非线程主要是指多个线程对同一个对象中的同一个实例变量进行操作时会出现值被更改 值不同步的情况
  • Java多线程:解决高并发环境下数据插入重复问题

    1 背景描述 应用框架 Spring SpringMVC Hibernate 数据库 Oracle11g 一家文学网站向我系统推多线程低并发推送数据 我这边观察日志和数据库 发现有一个作者被存储了2次到数据库中 按照程序的编写逻辑 重复的数
  • Java多线程的同步问题

    在多线程的编程环境中 可能会有两个或者更多的线程试图同时访问一个有限的资源 必须对这种潜在的资源冲突进行预防 解决办法 在线程使用一个资源的时候 我们为其加锁即可 访问资源的第一个线程为其加上锁以后 其它线程便不能访问那个资源 除非获得那个
  • 多线程案例:银行取钱

    不安全取钱 两个人去银行取钱 账户 银行取钱 给账户上锁 public class UnsafeBank public static void main String args 账户 Account3 account new Account
  • 多线程案例:购买车票

    购票案例 多线程同步 多线程的并发执行虽然可以提高程序的效率 但是 当多个线程去访问同一个资源时 也会引发一些安全问题 并发 同一个对象被多个线程同时操作 处理多线程问题时 多个线程访问同一个对象 并且某些线程还想修改这个对象 这时候我们就

随机推荐

  • 因果图设计用例的步骤_用例设计之因果图

    1 因果图的介绍 因果图法是一种利用图解法分析输入的各种组合情况 从而设计测试用例的方法 它适合于检查程序输入条件的各种组合情况 1 1 主要的因果关系 因果图中主要有两种节点 原因节点与结果节点 这两种节点分别有两种状态 0状态 1状态
  • 数据库基础知识

    数据库 1数据库联接操作 左连接 右链接 全链接的操作以及区别 问了数据库的三大范式 一编码 1登录 mysql uroot p 2查询数据库 mysql gt show database 3 选中数据库 use 数据库 4 创建数据库 m
  • 关于java.io.FileNotFoundException: class path resource [spring/springmvc.xml]异常的问题解决

    web xml配置文件如下图所示 但是老是报出以下的异常 org springframework beans factory BeanDefinitionStoreException IOException parsing XML docu
  • 【前端面试题】【Vue】v-if 和v-show有什么区别?

    Q v if 和v show有什么区别 A v if实际上操作的dom元素的销毁或者重建 会切换绑定的事件监听器和部分子组件 v show操作的是切换css的渲染 实际操作的是display属性 none block 如果需要频繁切换组件的
  • buuctf - web - [HCTF 2018]WarmUp

    老样子 F12 检查 发现 source php 被注释掉了 在 url 直接进行访问 可以看到是源代码 发现 high file 泄漏 访问 hint php 可以看到 flag 在那里 回头分析源代码 我们发现最底部的if语句为执行条件
  • Moonbeam社区提议新增收集人维护网络稳定,你怎么看?

    维护网络的稳定性和安全性一直是Moonbeam社区的关注重点 最近 Moonbeam社区成员Jim CertHum发布提案 呼吁新增4个活跃收集人至已有的Moonbeam网络收集人集 提升链上治理和网络出块效率 本次提案的重点 将网络中的活
  • Python安全攻防之第一章渗透测试概述

    渗透测试的具体方法 一 如何绕过CDN获取网站的真实IP地址 通过内部邮箱获取 一般情况下 邮箱服务器位于内网 我们可以通过注册邮箱或者订阅邮件 目的就是让对方给我自己发送邮件 这样 查看邮件的原始邮件头 就会发现邮件服务器的真实IP地址
  • docker - 在 alpine 上安装 MongoDB 的问题

    RUN echo http dl cdn alpinelinux org alpine v3 6 community gt gt etc apk repositories RUN apk update RUN apk add mongodb
  • 2019浙江ACM省赛

    目录 E Sequence in the Pocket J Welcome Party K Strings in the Pocket E Sequence in the Pocket 题意 给定一组数 每次操作 可以把一个数抽出 放在数组
  • postgres的时间转换

    天下苦postgres时间转换久已 最近在操作数据库时 遇到频繁的时间操作 每次弄完了就忘了 今天痛定思痛 下定决心 终于自己也受不了自己的lazy了 对postgres的时间操作进行一下总结 本文竟可能详尽的记录postgres中涉及到d
  • 对称二叉树(Leetcode &101)

    题目 101 对称二叉树 思路 使用层序遍历 遍历当前层的节点时 如该节点的左 右 孩子为空 在list中添加null 否则加入左 右 孩子的值 每遍历完一层则对当前list进行判断 这里判断我用了一个很笨的方法 前面记录下一层节点值时就设
  • tomcat内存(jvm)、并发、缓存优化

    内存调优 rem次调整为高并发内存调优 rem 以下配置为JVM参数调优 set JAVA OPTS server rem 以服务器模式启动 启动速度慢 但更稳定 性能更好 Xms8192M rem 由于本机内存为16G 这里就设置成8G
  • 最全ES6详解及用法

    最全ES6详解及用法 前言 babel babel使用方法 变量的定义 let const this 和作用域 do 顶层对象 global对象 import class JS中的原型 原型语言 prototype proto constr
  • 降低 CPU 占用率的方法

    CPU 占用率和什么有关 答 1 单位时间内执行的指令数目多少 用户时间 2 I O操作时间 等待时间 3 线程调度花费的时间 软 硬中断 优先级调整时间 举个例子 好比一个大人 每次来回可以扛一袋大米 每天的任务要求扛 60 袋 假定来回
  • 软件架构设计

    软件架构设计 一 引言 软件架构的概念 软件架构设计及其生命周期 1 需求分析阶段 需求分析与SA研究二者的联系 2 设计阶段 3 实现阶段 4 构件组装阶段 5 部署阶段 6 后开发阶段 软件架构的重要性 引言 从第一个程序被划分成模块开
  • Unity3D 场景切换

    在Unity3D中 一个场景在运行中切换到另一个场景的方法 1 在Unity中 File gt Build Settings gt 把要切换的场景添加到Scenes In Build下面 也可以Add Open Scenes 添加完关闭这个
  • 详解Java中的BIO、NIO、AIO

    1 详解Java中的BIO NIO AIO 1 1 引言 IO流是Java中比较难理解的一个知识点 但是IO流在实际的开发场景中经常会使用到 比如Dubbo底层就是NIO进行通讯 本文将介绍Java发展过程中出现的三种IO BIO NIO以
  • 渗透测试笔记(五)——XSS跨站脚本攻击

    XSS简介 XSS Cross Site Scripting 跨站脚本 为了避免与CSS混淆 所以简称XSS XSS是指恶意攻击者利用网站没有对用户提交数据进行转义处理或者过滤不足的缺点 进而添加一些代码 嵌入到Web页面中去 使别的用户访
  • 会话追踪技术:cookie、 URL重写

    servlet day4 会话追踪技术 cookie http协议的访问是无状态的访问 当前访问是不会知道之前访问的状态的 http协议的访问是不会帮我们保存访问的记录 痕迹的 有些时候需要记录之前的访问状态 比如在购物网站的时候 第一次访
  • 多线程与高并发编程进阶(二)

    前言 前文多线程与高并发入门中 已经介绍了多线程编程的目的以及实际应用中可能会遇到的问题 本文接着叙述关于多线程并发机制的底层原理 volatile以及synchronized 一般来说 Java代码从编写到最后的执行会经历以下的过程 Ja