并发编程线程安全性之可见性有序性

2024-02-26 23:52

本文主要是介绍并发编程线程安全性之可见性有序性,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

可见性

可见性: 就是说一个线程对共享变量的修改,另一个线程能够立刻看到

通俗点说,就是两个线程共享一个变量,无论哪一个线程修改了这个变量,另外一个线程都能够立刻看到上一个线程对这个变量的修改

产生线程安全问题的原因

计算机是利用CPU进行数据运算的,但是CPU只能对内存中的数据进行运算,对于磁盘中的数据,必须要先读取到内存,CPU才能进行运算。cpu,内存,磁盘都会影响计算机的处理性能,同时这三者之间有个核心的矛盾点,就是三者在处理速度上的差异。CPU的计算速度是非常快的,其次是内存、最后是IO设备(比如磁盘),也就是说CPU的计算速度是远远高于内存以及磁盘设备的I/O速度的。

为了平衡这三者之间的速度差异,最大化的利用CPU。所以在硬件层面、操作系统层面、编译器层面做出了很多的优化

  • CPU增加了高速缓存
  • 操作系统增加了进程、线程。通过CPU的时间片切换最大化的提升CPU的使用率
  • 编译器的指令优化,更合理的去利用好CPU的高速缓存

每一种优化,都会带来相应的问题,而这些问题是导致线程安全性问题的根源。

CPU高速缓存

解决的问题:CPU高速缓存的出现主要是为了解决CPU运算速度和内存读写速度不匹配的矛盾,因为CPU运算速度要比内存读写速度快很多,这样会使CPU花费很长时间等待数据到来或把数据写入内存。

打开任务管理器  可以看到CPU的3个缓存

思考: 有了高速缓存后会带来什么问题?

分析:CPU0和CPU1并行处理同时从内存中加载数据到缓存中,此时CPU0改变了这个值,会再同步到内存中,CPU1什么时候再读到这个最新的值是不确定的,因此会存在缓存一致性问题,那么应该怎么解决呢?

在解决这个问题之前,先来了解以下伪共享和缓存行填充

伪共享和缓存行填充

缓存行:CPU的缓存是由多个缓存行组成的,最小交互单元

伪共享:

当有两个线程读取同一缓存行的不同值时,就会去竞争这同一缓存行,这就是伪共享问题。

思考:怎么解决?对齐填充

缓存一致性问题和缓存一致性协议

  • 总线锁
  • 缓存锁
  • 缓存一致性协议(MESI  MOSI) MESI表示缓存的四种状态 修改 失效 独占 共享

如果是S状态 修改时 要先把其它缓存设置为失效 失效状态从内存中读

snoopy协议会监听总线上的事件

假设a=1这个数据要进行修改,刚读进来是共享状态,当需要修改时,会通过总线发送一个指令,让其它缓存失效,然后修改完后,其它缓存从内存中读数据,就解决了缓存一致性的问题。

CPU指令重排序 

cpu层面是如何导致指令重排序的? 如下图

CPU0要写入一个数据时,其它CPU在失效的时候,CPU0是处于阻塞状态的,要等到所有的缓存行失效后,再做写入操作,保证缓存一致性。 异步的思想就是会把数据加载到storebuffer中,继续执行其它的指令,等到其它的缓存行都失效后,再把数据从store buffe加载出来到缓存行

这段代码是如何存在指令重排序的

当CPU从内存中加载a=1时,会先加载到store buffer中,会让其它的缓存行失效,然后当前缓存行中的a=0(缓存一致性)变更为独占状态。然后CPU0从内存中读取b=0放到缓存行中,然后执行b=A+1此时计算出来的结果为1,然后此时其它CPU全部失效了,这时候CPU0再从store buffer中把a=1加载到缓存行,就造成了指令的重排序。

Store Forwarding

如何优化指令重排序?store forwarding就是CPU0从缓存行中读取数据,那么按照上面的例子,b=a+1加载到缓存行的数据,就是a=1了

分析: 假设a=0 存在于cpu1的缓存行中,b=0存在于cpu0的缓存行中 且都为独占状态。cpu0执行的两个代码为写操作 更新操作 CPU1只进行读。当CPU读取b的值时,就向cpu0发起请求,此时cpu0将b变更后设为共享状态。紧接着 a就在cpu1中,因此断言失败,此时 CPU1收到了CPU0的读请求,于是才让a=0缓存行失效,然后加载到cpu0中并变为独占状态后 在进行修改 。

引出的问题怎么办?

Invalid Queue

这个图就相当于 让其它缓存行失效的过程, 由同步 变为了 异步 直接丢到了invalidate中进行处理 提高效率。

CPU性能的博弈之路:

内存屏障

CPU层面不知道  什么时候不允许优化  什么时候优化

  • 读屏障
  • 写屏障
  • 全屏障

Lock: 缓存锁总线锁

在不同的CPU架构中,实现内存屏障的指令不同

JMM模型

Happens-Before模型(告诉你哪些场景不会存在指令重排序问题)

并不是所有的程序指令都会存在可见性或者指令重排序问题,其实质性描述的是可见性规则

规则1:程序顺序型规则(as-if-serial)

不管程序如何重排序,单线程的执行结果一定不会发生变化

规则2:传递性规则

如果A happens before B     B happens before C   那么A happens before C成立

规则3:volatile变量规则

规则4:监视器锁规则

规则5:Start规则

规则6:join规则

总结

可见性导致的原因 1.CPU的高速缓存 2.指令重排序

MESI协议保证缓存的一致性

指令重排序在不同的架构中有着不同的内存屏障指令来解决

这篇关于并发编程线程安全性之可见性有序性的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/750569

相关文章

Java中实现线程的创建和启动的方法

《Java中实现线程的创建和启动的方法》在Java中,实现线程的创建和启动是两个不同但紧密相关的概念,理解为什么要启动线程(调用start()方法)而非直接调用run()方法,是掌握多线程编程的关键,... 目录1. 线程的生命周期2. start() vs run() 的本质区别3. 为什么必须通过 st

Linux实现线程同步的多种方式汇总

《Linux实现线程同步的多种方式汇总》本文详细介绍了Linux下线程同步的多种方法,包括互斥锁、自旋锁、信号量以及它们的使用示例,通过这些同步机制,可以解决线程安全问题,防止资源竞争导致的错误,示例... 目录什么是线程同步?一、互斥锁(单人洗手间规则)适用场景:特点:二、条件变量(咖啡厅取餐系统)工作流

Java中常见队列举例详解(非线程安全)

《Java中常见队列举例详解(非线程安全)》队列用于模拟队列这种数据结构,队列通常是指先进先出的容器,:本文主要介绍Java中常见队列(非线程安全)的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录一.队列定义 二.常见接口 三.常见实现类3.1 ArrayDeque3.1.1 实现原理3.1.2

SpringBoot3中使用虚拟线程的完整步骤

《SpringBoot3中使用虚拟线程的完整步骤》在SpringBoot3中使用Java21+的虚拟线程(VirtualThreads)可以显著提升I/O密集型应用的并发能力,这篇文章为大家介绍了详细... 目录1. 环境准备2. 配置虚拟线程方式一:全局启用虚拟线程(Tomcat/Jetty)方式二:异步

如何解决Druid线程池Cause:java.sql.SQLRecoverableException:IO错误:Socket read timed out的问题

《如何解决Druid线程池Cause:java.sql.SQLRecoverableException:IO错误:Socketreadtimedout的问题》:本文主要介绍解决Druid线程... 目录异常信息触发场景找到版本发布更新的说明从版本更新信息可以看到该默认逻辑已经去除总结异常信息触发场景复

python多线程并发测试过程

《python多线程并发测试过程》:本文主要介绍python多线程并发测试过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、并发与并行?二、同步与异步的概念?三、线程与进程的区别?需求1:多线程执行不同任务需求2:多线程执行相同任务总结一、并发与并行?1、

Linux高并发场景下的网络参数调优实战指南

《Linux高并发场景下的网络参数调优实战指南》在高并发网络服务场景中,Linux内核的默认网络参数往往无法满足需求,导致性能瓶颈、连接超时甚至服务崩溃,本文基于真实案例分析,从参数解读、问题诊断到优... 目录一、问题背景:当并发连接遇上性能瓶颈1.1 案例环境1.2 初始参数分析二、深度诊断:连接状态与

Python 异步编程 asyncio简介及基本用法

《Python异步编程asyncio简介及基本用法》asyncio是Python的一个库,用于编写并发代码,使用协程、任务和Futures来处理I/O密集型和高延迟操作,本文给大家介绍Python... 目录1、asyncio是什么IO密集型任务特征2、怎么用1、基本用法2、关键字 async1、async

Java并发编程之如何优雅关闭钩子Shutdown Hook

《Java并发编程之如何优雅关闭钩子ShutdownHook》这篇文章主要为大家详细介绍了Java如何实现优雅关闭钩子ShutdownHook,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起... 目录关闭钩子简介关闭钩子应用场景数据库连接实战演示使用关闭钩子的注意事项开源框架中的关闭钩子机制1.

JAVA保证HashMap线程安全的几种方式

《JAVA保证HashMap线程安全的几种方式》HashMap是线程不安全的,这意味着如果多个线程并发地访问和修改同一个HashMap实例,可能会导致数据不一致和其他线程安全问题,本文主要介绍了JAV... 目录1. 使用 Collections.synchronizedMap2. 使用 Concurren