Java并发编程实战 学习笔记 Day7

news2025/1/16 8:02:39

并发编程是比较进阶的知识,涉及到很多底层的东西,学习起来是比较困难的。并发编程的bug更多的是偶发性的,很难复现,排查起来也很困难,要想快速解决问题,就要理解并发编程的本质,追本溯源,深入分析bug的源头。

从这篇文章开始,我将记录我学习Java并发编程的所有笔记,这是第一篇,希望可以帮助到更多需要的朋友。

我们都知道CPU、内存、I/O设备三者的速度差异,程序在运行过程中,大部分都要访问内存,有些也要访问I/O,程序的整体性能取决于最慢的I/O设备,为了合理利用CPU的高性能,平衡三者的速度差异,计算机体系结构、操作系统、编译程序都有贡献

  • CPU增加了缓存,以平衡和内存的速度差异;
  • 操作系统增加了进程、线程,来分时复用CPU,进而均衡CPU与I/O设备的速度差异;
  • 编译程序优化指令执行次序,使得缓存能够得到更加合理利用
源头之一:缓存导致的可见性问题

单核时代,所有的线程都是在一颗CPU上执行,CPU缓存和内存的数据一致性很容易保证,因为所有的线程操作的都是同一个CPU缓存,一个线程对缓存的操在对其他线程来说是一定可见的。

多核时代,每颗CPU都有自己的缓存,这是CPU缓存与内存的数据一致性就不容易解决了,当多个线程在不同的CPU上执行时,这些线程操作的是不同的CPU缓存。

源头二:线程切换带来的原子性问题

Java并发编程都是基于多线程的,自然也会涉及到任务切换。任务切换的时机大多数都是在时间片结束的时候,我们现在基本都使用高级语言编程,高级语言里一条语句往往需要多条CPU指令完成,例如代码:count += 1,至少三条CPU指令。

  • 1.首先,需要把变量count从内存加载到CPU寄存器
  • 2.之后,在寄存器中执行+1操作
  • 3.将结构写入内存。

操作系统做任务切换,可以发生在任何一条CPU指令执行完,对于上面的三条指令来说,我们假设count=0,如果线程A在指令1执行完成后线程切换,线程B开始执行,线程B把三条指令都执行完成,再切换回线程A继续执行,此时线程A执行指令2,3,最终得到的结果还是1,而不是我们期望的2

image

造成这个问题的原因是因为我们以为count+=1这个操作是一个整体,其实再多线程执行的时候,线程的切换会发生在count+=1之前也可能是之后,就不会发生在中间。我们把一个或多个操作在CPU执行的过程中不被中断的特性称为原子性

源头三:编译优化带来的有序性问题

编译器在百衲衣程序的时候,为了优化性能,会改变语句中的先后顺序,绝大部分情况下都是没有问题的,不会影响程序的最终结果,不过有时候编译器及解释器优化导致的bug也是改到头皮发麻。

以Java中的双重检查创建单例对象代码为例:

public class Singleton {
  static Singleton instance;
  static Singleton getInstance(){
    if (instance == null) {
      synchronized(Singleton.class) {
        if (instance == null)
          instance = new Singleton();
        }
    }
    return instance;
  }
}

在获取实例 getInstance() 的方法中,我们首先判断 instance 是否为空,如果为空,则锁定 Singleton.class 并再次检查 instance 是否为空,如果还为空则创建 Singleton 的一个实例。

假设同时两个线程A、B同时调用getInstance()方法,此时instance==null,于是对Singleton.class加锁,此时,JVM保证只有一个线程能够加锁成功,另外一个线程则会等待(假设是线程 B);线程 A 会创建一个 Singleton 实例,之后释放锁,锁释放后,线程 B 被唤醒,线程 B 再次尝试加锁,此时是可以加锁成功的,加锁成功后,线程 B 检查 instance == null 时会发现,已经创建过 Singleton 实例了,所以线程 B 不会再创建一个 Singleton 实例。

看上去问题不大,实际上还是存在问题的,问题就出在new操作不是原子的,我们以为的new操作:

  1. 分配一块内存M
  2. 在内存M上初始化Singleton对象
  3. 将M的地址复制给instance变量

实际经过编译器优化之后的执行路径可能是:1-3-2

优化之后,假设线程A执行到第2步的是,发生了线程切换,切换到线程B,线程B也执行getInstance()方法,那么线程B执行,发现instance != null,直接返回使用,此时instance还未进行初始化,此时就可能发生空指针异常。

学好并发编程,就要深刻理解可见性、原子性、有序性在并发场景下的原理,在面对很多问题的时候就会迎刃而解。

学习来源:极客时间

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

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

相关文章

Android ANR bugreport log分析

最近工作中频繁遇到设备ANR问题,而且是概率性的那种,于是决定花点时间找找规律复现分析下 说道这里,抓日志是问题解决的最有效途径,这里不得不说一下 bugreport log,其实网上关于它的分析方法有很多,在此仅仅是为了记录…

小型水库在线监测解决方案

一、方案背景水利部全国水利普查显示我国共有水库98002座,其中小型水库93308座。水库对防汛减灾、供水保障和农业灌溉等至关重要,水库的稳定运行关系到下游人民群众的生命财产安全,关系到当地经济发展和社会稳定。在2020年11月18日国务院常务…

EasyVim:简单强大的VIM配置

EasyVim 简单强大的vim配置,熟练后可大大提高开发效率(VS Code的两倍以上)。 安装 安装过程需要从github下载很多插件,国内尽量挂VPN git clone https://github.com/yuesong-feng/EasyVim cd EasyVim/ sh ./install.sh vim :P…

JavaScript基础(16)_数组方法、数组遍历、foreach

数组方法unshift():向数组开头添加一个或多个元素,并返回新的数组长度。向前边插入元素以后,其他的元素索引会依次调整。push():向数组的末尾添加一个或多个元素,并返回数组新的长度。该方法会将数组新的长度作为返回值返回。shift():删除数组…

【工具推荐】 Obsidian 插件 Obsidian to Flomo 一键同步内容到 Flomo 插件

目录一、Obsidian、Flomo、Obsidian to Flomo 他们都是什么?1. 什么是 Obsidian ?2. 什么是Flomo ?3. 什么是Obsidian to Flomo ?二、如何安装 Obsidian to Flomo ?三、插件使用方法1. 启用插件并配置API四、如何使用插…

《2022大数据产业年度创新技术突破》榜重磅发布丨金猿奖

‍年度金猿榜单/奖项本届“数据猿年度金猿策划活动——2022大数据产业创新技术突破榜单/奖项”由金猿&数据猿共同推出。‍数据智能产业创新服务媒体——聚焦数智 改变商业2022年下半年度,由数据猿、金猿组委会共同推出的第五届 “年度金猿季大型主题策划活动”…

Unity Netcode for GameObjects多人联机(源文件)

一、安装Netcode for Gameobjects Netcode for Gameobjects是Unity新推出的联机解决方案,该解决方案目前处于初期,相关功能不是很完善,但是用起来还不错。下面介绍一下相关用法。 首先下载安装Netcode for Gameobjects,其官方网站…

Elasticsearch入门介绍及Linux安装

前言Elasticsearch是一款分布式高性能的全文搜索引擎,为什么会需要这个呢,像我们平常使用的最多的存储工具就是Mysql,在业界也是非常有名的,我们大部分结构化数据都是用它来存储的,sql语言的操作也是非常方便&#xff…

助力“智慧港口”建设,北部湾港联合美创打造的主数据管理系统上线!

数字化浪潮下,港口企业发展将致力于以数据价值提升和数据价值创造为目标,打造新型“智慧港口”,实现更加经济的港口资源统筹高效利用并降低各类成本,企业通过逐步转变信息资源管理工作模式和利用方式,深度应用新一代信…

在GCP上创建Cloud SQL的三种方式(Console,gcloud,Terraform)

1 简介 Cloud SQL 是GCP上的关系型数据库,常用的有三种方式来创建: (1) 界面操作 (2) 命令行 gcloud (3) Terraform 在开始之前,可以查看:《初始化一个GCP项目并用gcloud访问操作》。 2 GCP 操作界面 登陆GCP,选…

Chevereto V4 首页显示图片托管数量的实现方法

博主最近一直想在Chevereto V4 首页显示图片托管数量,但是网上的方法只针对于Chevereto V3,V4却没有相关教程,查询谷歌也无结果,经博主查询大量案例今天为大家分享一下 Chevereto V4 首页显示图片托管数量的实现方法。1. 修改后台…

70.语义分割和数据集

在 之前讨论的目标检测问题中,我们一直使用方形边界框来标注和预测图像中的目标。 本节将探讨语义分割(semantic segmentation)问题,它重点关注于如何将图像分割成属于不同语义类别的区域。 与目标检测不同,语义分割可…

20230112编译AIO-3568J的Buildroot(rk356x_linux_release_v1.3.0b_20221213)

20230112编译AIO-3568J的Buildroot(rk356x_linux_release_v1.3.0b_20221213) 2023/1/12 20:40 当前可以拿到的Buildroot的SDK; rk356x_linux_release_v1.0.0_20210511_split_dir rk356x_linux_release_v1.2.0_20211019_split_dir rk356x_linu…

Ventoy主题美化,以及自行制作方法

Ventoy是基于grub2 所制作的,所以可以自行制作或者将现成的主题套用到Ventoy 方法一: 主题下载地址 上面是两个可以直接使用的Ventoy主题地址,然后下载下来解压文件,我们可以得到 接着往下走,我们可以的得到 现在我们…

Oracle Apex低码平台-定制验证方案

Oracle Apex低码平台-定制验证方案 0 APEX简介: Oracle APEX 是一个低代码开发平台,您可以在该平台上构建可扩展的安全企业应用程序。这些应用程序具有先进的功能,而且可以在任何地方部署。 构建企业应用速度提高 20 倍,代码减…

Dubbo服务降级

Dubbo服务降级 1. 为什么需要服务降级 RPC 是解决分布式系统通信问题的一大利器,而分布式系统的一大特点就是高并发,所以说 RPC 也会面临高并发的场景。在这样的情况下,我们提供服务的每个服务节点就都可能由于访问量过大而引起一系列的问题…

Chrome浏览器插件推荐【第一期】

1、Tampermonkey Tampermonkey(油猴)是一款免费的浏览器扩展和最为流行的用户脚本管理器,它适用于 Chrome, Microsoft Edge, Safari, Opera Next, 和 Firefox。虽然有些受支持的浏览器拥有原生的用户脚本支持,但 Tampermonkey 将在…

Webpack的应用

处理css文件 总共有src目录下的index.css和index.js、同根的index.html和webpack.config.js文件,然后npm init之后生成package.json文件,npm install后生成package-lock.json文件,最后npm run webpack之后有dist目录下各种文件 index.js i…

nodejs使JWT(全)

Token token表示令牌,用户的登录凭证。 基于 Token 的身份验证方法,使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录。大概的流程是这样的: 客户端使用用户名跟密码请求登录服务端收到请求,去验证…

css动画效果之transform

transformTransform属性应用于元素的2D或3D转换。这个属性允许你将元素旋转,缩放,移动,倾斜等。旋转rotate、扭曲skew、移动translate、缩放scale、矩阵变形matrix属性名扩展写法属性含义none定义不进行转换。rotaterotateX()(3D写…