1.并发问题产生的三大根源与解决sync锁 目录 1. 并发问题产生的三大根源 1 1.1. 原子性 CPU缓存会导致可见问题 指令重排序 1 2. 并发问题根源之一:CPU切换线程执导致的
- 并发问题产生的三大根源与解决sync锁
目录
1. 并发问题产生的三大根源 1
1.1. 原子性 CPU缓存会导致可见问题 指令重排序 1
2. 并发问题根源之一:CPU切换线程执导致的原子性问题 1
2.1. 加锁解决原子性 和atomcatic类型 2
3. 并发问题根源之二:缓存导致的可见性问题 2
3.1. 二、高速缓存的产生 缓存失效问题 2
3.2. Sync和Volide解决可见性 3
4. 三、指令优化 3
4.1. 并发问题根源之三:指令优化导致的重排序问题 4
5. Synchronized和volatile 对区别 4
5.1. 最简单方法加锁 sync 5
- 原子性 CPU缓存会导致可见问题 指令重排序
因为CPU可以切换线程执行指令所以会导致我们的一些操作会失去原子性,因为CPU缓存会导致可见问题、还有CPU的一些指令重排序优化也会导致一些问题,关于这些我们将在并发问题的三大根源一文中继续探讨。
- 并发问题根源之一:CPU切换线程执导致的原子性问题
首先我们先理解什么叫原子性,原子性就指是把一个操作或者多个操作视为一个整体,在执行的过程不能被中断的特性叫原子性。
- 加锁解决原子性 和atomcatic类型
- 并发问题根源之二:缓存导致的可见性问题
在有了高速缓存之后,CPU的执行操作数据的过程会是这样的,CPU首先会从内存把数据拷贝到CPU缓存区。
然后CPU再对缓存里面的数据进行更新等操作,最后CPU把缓存区里面的数据更新到内存。
磁盘、内存、CPU缓存会按如下形式协作。
- 二、高速缓存的产生 缓存失效问题
告诉cache和多cpu cache都会导致这个问题。
为了减少CPU等待IO的时间,让CPU有更多的时间是花在运算上,最简单的思路就是减少IO等待的时间,基于这个思路所以就有了高速缓存增加了高速缓存(L1,L2,L3,主存)。
当处理器发出内存访问请求时,会先查看高速缓存内是否有请求数据。如果存在(命中),则不需要访问内存直接返回该数据;如果不存在(失效),则要先把内存中的相应数据载入缓存,再将其返回处理器。
缓存导致的可见性问题就是指我们在操作CPU缓存过程中,由于多个CPU缓存之间独立不可见的特性,导致共享变量的操作结果无法预期。
在单核CPU时代,因为只有一个核心控制器,所以只会有一个CPU缓存区,这时各个线程访问的CPU缓存也都是同一个,在这种情况一个线程把共享变量更新到CPU缓存后另外一个线程是可以马上看见的,因为他们操作的是同一个缓存,所以他们操作后的结果不存在可见性问题。
而随着CPU的发展,CPU逐渐发展成了多核,CPU可以同时使用多个核心控制器执行线程任务,当然CPU处理同时处理线程任务的速度也越来越快了,但随之也产生了一个问题,多核CPU每个核心控制器工作的时候都会有自己独立的CPU缓存,每个核心控制器都执行任务的时候都是操作的自己的CPU缓存,CPU1与CPU2它们之间的缓存是相互不可见的。
这种情况下多个线程操作共享变量就因为缓存不可见而带来问题,多线程的情况下线程并不一定是在同一个CUP上执行,它们如果同时操作一个共享变量,但因为在不同的CPU执行所以他们只能查看和更新自己CPU缓存里的变量值,线程各自的执行结果对于别的线程来说是不可见的,所以在并发的情况下会因为这种缓存不可见的情况会导致问题出现。
- Sync和Volide解决可见性
- 三、指令优化
进程和线程本质上是增加并行的任务数量来提升CPU的利用率,缓存是通过把IO时间减少来提升CPU的利用率,而指令顺序优化的初衷的初衷就是想通过调整CPU指令的执行顺序和异步化的操作来提升CPU执行指令任务的效率。
指令顺序优化可能发生在编译、CPU指令执行、缓存优化几个阶,其优化原则就是只要能保证重排序后不影响单线程的运行结果,那么就允许指令重排序的发生。其重排序的大体逻辑就是优先把CPU比较耗时的指令放到最先执行,然后在这些指令执行的空余时间来执行其他指令,就像我们做菜的时候会把熟的最慢的菜最先开始煮,然后在这个菜熟的时间段去做其它的菜,通过这种方式减少CPU的等待,更好的利用CPU的资源。
- 并发问题根源之三:指令优化导致的重排序问题
下面的程序代码如果init()方法的代码经过了指令重排序后,两个方法在两个不同的线程里面调用就可能出现问题。
- Synchronized和volatile 对区别
- 最简单方法加锁 sync
volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
volatile按照语法规范,只是表明同一线程中对两个volatile的操作要保序。拿volatile去解决多线程问题,其实是依赖于“锁操作也是对volatile进行操作”这一假设得到的取巧的结果,如果你没加锁,光靠volatile也是错的。
现在是不推荐拿volatile解决多线程问题的,这是个历史遗留问题。请看新一点的、更权威更正确的教材。
如果需要一个更大范围的原子性可以使用synchronized来实现,synchronized块之间的操作。
可以使用volatile保证可见性,也可以使用关键字synchronized和final。
有序性:在本线程中所有的操作都是有序的;在另一个线程中,看来所有的操作都是无序的,就可需要使用具有天然有序性的volatile保持有序性,因为其禁止重排序。
Volatile 的特性:
保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。(实现可见性)
禁止进行指令重排序。(实现有序性)
volatile 只能保证对单次读/写的原子性,i++ 这种操作不能保证原子性
Synchronized
Synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法。Synchronized的作用主要有三个:
原子性:确保线程互斥的访问同步代码;
可见性:保证共享变量的修改能够及时可见,其实是通过Java内存模型中的 “对一个变量unlock操作之前,必须要同步到主内存中;如果对一个变量进行lock操作,则将会清空工作内存中此变量的值,在执行引擎使用此变量前,需要重新从主内存中load操作或assign操作初始化变量值” 来保证的
有序性:有效解决重排序问题,即 “一个unlock操作先行发生(happen-before)于后面对同一个锁的lock操作”;
首先想到是那就在使用synchronized作用在静态方法:
虽然这样简单粗暴解决,但会导致这个方法比较效率低效,导致程序性能严重下降,那是不是还有其他更优的解决方案呢?
那么问题找到了,那怎么去解决呢?那就禁止不允许初始化阶段步骤2 、3发生重排序,刚好Volatile 禁止指令重排,从而使得双重检测真正发挥作用。
volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
使用volatile而不是synchronized的唯一安全的情况是类中只有一个可变的域
1.并发问题产生的三大根源与解决sync锁 目录 1. 并发问题产生的三大根源 1 1.1. 原子性 CPU缓存会导致可见问题 指令重排序 1 2. 并发问题根源之一:CPU切换线程执导致的相关推荐
- java volidate线程安全_03.(多线程与并发)面试题-02--Volidate的原理和指令重排序
线程栈(线程的工作内存)保存了线程运行时候变量值信息.当线程访问某一个对象时候值的时候,首先通过对象的引用找到对应在堆内存的变量的值,然后把堆内存变量的具体值load到线程本地内存中,建立一个变量副本 ...
- 【Java 并发编程】指令重排序规范 ( happens-before 先行发生原则 )
文章目录 一.指令重排序规范 二.happens-before 先行发生原则 一.指令重排序规范 指令重排指的是 , 线程中如果两行代码 没有逻辑上的上下关系 , 可以对代码进行 重新排序 ; JVM ...
- 【Java 并发编程】线程指令重排序问题 ( 指令重排序规范 | volatile 关键字禁止指令重排序 )
文章目录 总结 一.指令重排序规范 二.指令重排序示例 总结 Java 并发的 333 特性 : 原子性 : 每个操作都是 不可拆分的原子操作 ; 在线程中进行 a++ 就不是原子操作 , 该操作分为 ...
- CPU流水线与指令重排序
目录 1. Compiler Reordering 2. CPU 流水线 2.1. 从汽车装配谈起 2.2. 现代CPU的流水线 3. 超长流水线的瓶颈 3.1. 性能瓶颈 3.2. 功耗瓶颈 3.3 ...
- JVM并发机制探讨—内存模型、内存可见性和指令重排序
并发本来就是个有意思的问题,尤其是现在又流行这么一句话:"高帅富加机器,穷矮搓搞优化".从这句话可以看到,无论是高帅富还是穷矮搓都需要深入理解并发编程,高帅富加多了机器,需要协调多 ...
- Java并发编程之指令重排序
在我们面试过程中,通常避免不了会被问到什么是指令重排序?本文就这个问题进行探索. 重排序 前言 一.重排序种类 二.happens-before 三.重排序 1.数据依赖性 2. as-if-seri ...
- CPU乱序执行(指令重排序)
CPU的速度至少比内存快100倍,为了提升效率,会打乱原来的执行效率,会在一条指令执行过程中(比如去内存读数据,大概慢100多倍),去同时执行另一条指令,前提是两条指令没有依赖关系(洗茶壶/烧水-茶叶 ...
- java排序为什么会出现多次排序结果不一样_并发理论基础:指令重排序问题
为什么需要对指令进行重排序? 其实说到底都是源于对性能的优化,CPU运行效率 相比缓存.内存.硬盘IO之间效率有着指数级的差别,CPU作为系统的宝贵资源,那么如何更好的优化和利用这个资源就能提升整个计 ...
- java基础—java内存模型(JMM)CPU架构、缓存一致性、重排序、JMM的实现、JMM保证可见性、有序性问题的详解
java基础-JMM(CPU架构.JMM保证可见性.有序性) 文章目录 java基础-JMM(CPU架构.JMM保证可见性.有序性) CPU架构 说一说现在计算机存储器的结构层次 使用CPU缓存,会导 ...
- CPU 缓存如何影响你的 Go 程序性能
小菜刀最近在medium上阅读了一篇高赞文章<Go and CPU Caches>,其地址为https://teivah.medium.com/go-and-cpu-caches-af5d ...
最新文章
- LA 5717枚举+最小生成树回路性质
- 强势推荐8个顶级好用的软件,堪称神器
- ODOO权限管理,在两个方面设置权限
- GraphQL学习过程应该是这样的
- 云计算网络基础第八天
- 寻找最大的K个数,Top K问题的堆实现
- ubuntu16安装中文版和windows关联
- 2.3 C#中的数组
- 内网渗透--获取其他电脑相册
- WeihanLi.Npoi 1.21.0 Released
- leetcode 766. 托普利茨矩阵
- jQuery Ajax 如何设置Timeout
- win10主题更换_还不升级? win10精简版不到10G,运行比win7还快,旧电脑的福音
- 添加mysql库路径_修改MySQL数据库存放路径
- 利用Session实现一次性验证码(多学一招)
- 从SQL Server生成文档
- Git前世今生-版本控制软件的发展
- 在Ubuntu下使用Apt-Get安装Google Chrome
- C 中细节问题的试题
- Phison(群联) PS2251-70等系列量产工具,U盘量产修复,另外一芯片的量产工具
热门文章
- hltm连接css的link,CSS 链接(link)
- fedora html 编辑器,分享|在 Fedora 上使用 Pitivi 编辑视频
- cher怎么翻译中文_中文翻译法语收费标准是怎么定的
- 空间三维直线拟合matlab代码
- sh脚本异常:/bin/sh^M:bad interpreter: No such file ...
- 【死磕 Spring】—– IOC 之 bean 的初始化
- each iteration和 each occurrunce的区别
- c#简要概括面向对象的三大特征(三)
- 几何画板中该如何插入公式
- 轻量级前端MVVM框架avalon - 初步接触