并发编程核心问题-可见性,原子性,有序性

1、可见性

如何产生的可见性问题?

java多线程在工作时,现将主内存中的数据读到线程工作内存(缓存),然后在工作内存中,对数据进行操作,操作完成后,再将数据写回到主内存。

  • 产生一个可见性问题?
    2线程中看不到1线程中操作过的数据,操作系统可能会对指令的执行先后顺序进行重新排序执行。导致内存中a=1,而不是我们期望的a=2;
    在这里插入图片描述

概述

对于如今的多核处理器,每颗 CPU 都有自己的缓存,而缓存仅仅对它所在的处理器可见,CPU 缓存与内存的数据不容易保证一致。
为了避免处理器停顿下来等待向内存写入数据而产生的延迟,处理器使用写缓冲区来临时保存向内存写入的数据。写缓冲区合并对同一内存地址的多次写,并以批处理的方式刷新,也就是说写缓冲区不会即时将数据刷新到主内存中。缓存不能及时刷新导致了可见性问题。

2、有序性

  • 有序性指的是程序按照代码的先后顺序执行
  • 编译器为了优化性能,有时候会改变程序中语句的先后顺序。
    在这里插入图片描述
  • Java 内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性

3、原子性

线程切换带来的原子性(不可拆分)问题
一个或多个操作在 CPU 执行的过程中不被中断的特性,我们称为原子性.
eg:
i++(i=i+1)操作,分为先计算,后赋值,++操作不是原子操作,cpu在执行时可能会分成两步来执行。

  • CPU 能保证的原子操作是 CPU 指令级别的,而不是高级语言的操作符。线程
    切换导致了原子性问题
    在这里插入图片描述
    Java 并发程序都是基于多线程的,自然也会涉及到任务切换,任务切换的时机大多数是在时间片结束的时候。我们现在基本都使用高级语言编程,高级语言里一条语句往往需要多条 CPU 指令完成。如 count++,至少需要三条 CPU指令。
    指令 1:首先,需要把变量 count 从内存加载到工作内存;
    指令 2:之后,在工作内存执行 +1 操作;
    指令 3:最后,将结果写入内存;
    还是以上面的 count++ 为例。两个线程 A 和 B 同时执行 count++,即便 count 使用 volatile 修辞,我们预期的结果值是 2,但实际可能是 1。
    在这里插入图片描述

4、总结(并发编程核心问题)

  1. 线程本地缓存,会导致可见性问题
  2. 线程切换执行,会导致原子性问题
  3. 编译优化重排指令,会带来有序性问题

5、volatile关键字

  1. 被volatile修饰后的共享变量,在一个线程中操作后,可以保证在另一个线程中立即可见的。
  2. 禁止进行指令重排序。
  3. volatile 不能保证对变量操作的原子性。
    eg:
package com.ffyc.database.volatiledemo;

public class ThreadDemo implements  Runnable{
   

    /*
       volatile 修饰的变量,在一个线程中被修改后,对其它线程立即**可见**、禁止cpu对指令重排序
     */
    private vo
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值