1. 并发问题产生的三大根源与解决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

  1. 原子性   CPU缓存会导致可见问题  指令重排序

因为CPU可以切换线程执行指令所以会导致我们的一些操作会失去原子性,因为CPU缓存会导致可见问题、还有CPU的一些指令重排序优化也会导致一些问题,关于这些我们将在并发问题的三大根源一文中继续探讨。

  1. 并发问题根源之一:CPU切换线程执导致的原子性问题

首先我们先理解什么叫原子性,原子性就指是把一个操作或者多个操作视为一个整体,在执行的过程不能被中断的特性叫原子性。

  1. 加锁解决原子性 和atomcatic类型
  1. 并发问题根源之二:缓存导致的可见性问题

在有了高速缓存之后,CPU的执行操作数据的过程会是这样的,CPU首先会从内存把数据拷贝到CPU缓存区。

然后CPU再对缓存里面的数据进行更新等操作,最后CPU把缓存区里面的数据更新到内存。

磁盘、内存、CPU缓存会按如下形式协作。

  1. 二、高速缓存的产生 缓存失效问题

告诉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缓存里的变量值,线程各自的执行结果对于别的线程来说是不可见的,所以在并发的情况下会因为这种缓存不可见的情况会导致问题出现。

  1. Sync和Volide解决可见性
  1. 三、指令优化

进程和线程本质上是增加并行的任务数量来提升CPU的利用率,缓存是通过把IO时间减少来提升CPU的利用率,而指令顺序优化的初衷的初衷就是想通过调整CPU指令的执行顺序和异步化的操作来提升CPU执行指令任务的效率。

指令顺序优化可能发生在编译、CPU指令执行、缓存优化几个阶,其优化原则就是只要能保证重排序后不影响单线程的运行结果,那么就允许指令重排序的发生。其重排序的大体逻辑就是优先把CPU比较耗时的指令放到最先执行,然后在这些指令执行的空余时间来执行其他指令,就像我们做菜的时候会把熟的最慢的菜最先开始煮,然后在这个菜熟的时间段去做其它的菜,通过这种方式减少CPU的等待,更好的利用CPU的资源。

  1. 并发问题根源之三:指令优化导致的重排序问题

下面的程序代码如果init()方法的代码经过了指令重排序后,两个方法在两个不同的线程里面调用就可能出现问题。

  1. Synchronizedvolatile 对区别

    1. 最简单方法加锁 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切换线程执导致的相关推荐

  1. java volidate线程安全_03.(多线程与并发)面试题-02--Volidate的原理和指令重排序

    线程栈(线程的工作内存)保存了线程运行时候变量值信息.当线程访问某一个对象时候值的时候,首先通过对象的引用找到对应在堆内存的变量的值,然后把堆内存变量的具体值load到线程本地内存中,建立一个变量副本 ...

  2. 【Java 并发编程】指令重排序规范 ( happens-before 先行发生原则 )

    文章目录 一.指令重排序规范 二.happens-before 先行发生原则 一.指令重排序规范 指令重排指的是 , 线程中如果两行代码 没有逻辑上的上下关系 , 可以对代码进行 重新排序 ; JVM ...

  3. 【Java 并发编程】线程指令重排序问题 ( 指令重排序规范 | volatile 关键字禁止指令重排序 )

    文章目录 总结 一.指令重排序规范 二.指令重排序示例 总结 Java 并发的 333 特性 : 原子性 : 每个操作都是 不可拆分的原子操作 ; 在线程中进行 a++ 就不是原子操作 , 该操作分为 ...

  4. CPU流水线与指令重排序

    目录 1. Compiler Reordering 2. CPU 流水线 2.1. 从汽车装配谈起 2.2. 现代CPU的流水线 3. 超长流水线的瓶颈 3.1. 性能瓶颈 3.2. 功耗瓶颈 3.3 ...

  5. JVM并发机制探讨—内存模型、内存可见性和指令重排序

    并发本来就是个有意思的问题,尤其是现在又流行这么一句话:"高帅富加机器,穷矮搓搞优化".从这句话可以看到,无论是高帅富还是穷矮搓都需要深入理解并发编程,高帅富加多了机器,需要协调多 ...

  6. Java并发编程之指令重排序

    在我们面试过程中,通常避免不了会被问到什么是指令重排序?本文就这个问题进行探索. 重排序 前言 一.重排序种类 二.happens-before 三.重排序 1.数据依赖性 2. as-if-seri ...

  7. CPU乱序执行(指令重排序)

    CPU的速度至少比内存快100倍,为了提升效率,会打乱原来的执行效率,会在一条指令执行过程中(比如去内存读数据,大概慢100多倍),去同时执行另一条指令,前提是两条指令没有依赖关系(洗茶壶/烧水-茶叶 ...

  8. java排序为什么会出现多次排序结果不一样_并发理论基础:指令重排序问题

    为什么需要对指令进行重排序? 其实说到底都是源于对性能的优化,CPU运行效率 相比缓存.内存.硬盘IO之间效率有着指数级的差别,CPU作为系统的宝贵资源,那么如何更好的优化和利用这个资源就能提升整个计 ...

  9. java基础—java内存模型(JMM)CPU架构、缓存一致性、重排序、JMM的实现、JMM保证可见性、有序性问题的详解

    java基础-JMM(CPU架构.JMM保证可见性.有序性) 文章目录 java基础-JMM(CPU架构.JMM保证可见性.有序性) CPU架构 说一说现在计算机存储器的结构层次 使用CPU缓存,会导 ...

  10. CPU 缓存如何影响你的 Go 程序性能

    小菜刀最近在medium上阅读了一篇高赞文章<Go and CPU Caches>,其地址为https://teivah.medium.com/go-and-cpu-caches-af5d ...

最新文章

  1. LA 5717枚举+最小生成树回路性质
  2. 强势推荐8个顶级好用的软件,堪称神器
  3. ODOO权限管理,在两个方面设置权限
  4. GraphQL学习过程应该是这样的
  5. 云计算网络基础第八天
  6. 寻找最大的K个数,Top K问题的堆实现
  7. ubuntu16安装中文版和windows关联
  8. 2.3 C#中的数组
  9. 内网渗透--获取其他电脑相册
  10. WeihanLi.Npoi 1.21.0 Released
  11. leetcode 766. 托普利茨矩阵
  12. jQuery Ajax 如何设置Timeout
  13. win10主题更换_还不升级? win10精简版不到10G,运行比win7还快,旧电脑的福音
  14. 添加mysql库路径_修改MySQL数据库存放路径
  15. 利用Session实现一次性验证码(多学一招)
  16. 从SQL Server生成文档
  17. Git前世今生-版本控制软件的发展
  18. 在Ubuntu下使用Apt-Get安装Google Chrome
  19. C 中细节问题的试题
  20. Phison(群联) PS2251-70等系列量产工具,U盘量产修复,另外一芯片的量产工具

热门文章

  1. hltm连接css的link,CSS 链接(link)
  2. fedora html 编辑器,分享|在 Fedora 上使用 Pitivi 编辑视频
  3. cher怎么翻译中文_中文翻译法语收费标准是怎么定的
  4. 空间三维直线拟合matlab代码
  5. sh脚本异常:/bin/sh^M:bad interpreter: No such file ...
  6. 【死磕 Spring】—– IOC 之 bean 的初始化
  7. each iteration和 each occurrunce的区别
  8. c#简要概括面向对象的三大特征(三)
  9. 几何画板中该如何插入公式
  10. 轻量级前端MVVM框架avalon - 初步接触