Java 并发编程 之 volatile(三)

都是缓存惹的祸害

这里写图片描述

这里写图片描述

这里写图片描述

缓存一致性协议: 当某个CPU核心写数据时,如果发现这个变量为共享变量,即在其他CPU缓存中也有副本,就会通知其他CPU将改变量的缓存置为无效, 其他CPU需要从内存重新读取。

MESI 的状态

这里写图片描述

案例:

这里写图片描述
数据有效,数据和内存中的数据一致,数据只存在于CPU核心1中的Cache中

这里写图片描述
数据有效,数据和内存中的数据一致,数据存在于CPU核心1和CPU核心2的Cache中。

这里写图片描述

当某一个CPU核心发生修改:CPU核心1中数据有效,数据被修改了,和内存中的数据不一致,数据只存在于本CPU的Cache中。CPU核心中的Cache数据无效。

这里写图片描述

当修改完成后会将缓存更新,即回到S状态。

并发编程的概念: 无状态

无状态对象一定是线程安全的

这里写图片描述

并发编程的概念: 共享状态

由于count++ 不是一个原子操作,会导致线程安全问题。

这里写图片描述

并发编程的概念: 原子性

  • 转账的例子
    • A 向 B转账
    • A = A-100
    • B = B+100
  • 要么全做,要么全不做, 不能出现做一半的情况

并发编程的概念: 可见性

  • 多个线程访问一个共享变量时, 一个线程对变量的修改,其他线程能否立刻看到刚修改的值。
    这里写图片描述

内存中共享变量i的值为0 ,在没有缓存一致性的情况下, 线程1对变量i的修改对线程2是不可见的。

并发编程的概念: 有序性

这里写图片描述

指令重排

  • 为了提高性能,编译器和处理器常常会对指令做重排序
    • 编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。
    • 指令级并行的重排序。现代处理器采用了指令级并行技术(Instruction-Level Parallelism, ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
    • 内存系统的重排序。由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。

并发编程

  • 在存在共享数据的情况下, 并发程序想要正确地执行,必须要保证原子性、可见性以及有序性。只要有一个没有被保证,就有可能会导致程序运行不正确。

  • 编写线程安全的代码,本质上就是管理对状态(state)的访问,而且通常都是共享的、可变的状态。这里的状态通常是对象的变量(静态变量和实例变量)

Java 内存模型

所有变量都在主存,必须读到工作内存线程才能操作

这里写图片描述

例子

这里写图片描述

  • 线程t 读取 stop 的值(false) 并且放到自己的工作内存中
  • 主线程也读取stop的值(false), 放到自己的工作内存中, 并且改为true
    但是线程t 中工作内存中的stop 依然是false;

  • 一个线程对一个共享变量的修改对另外一个线程是不可见的!

用volatile实现可见性

  • volatile关键字
    • volatile boolean stop = false;
  • 当读写一个volatile变量的时候, 每次都从主内存读写, 而不是工作内存

  • volatile实现了可见性

    • 但是能实现原子性吗?

例子:
这里写图片描述

分析:

  • 问题就出在 value ++ 上, 这不是一个原子操作
    • 线程A从主内存读取最新的value值(依照volatile的原则)
    • 线程A对value 值加1
    • 线程A把value的值写回主内存
  • 在线程A执行到第二步的时候, 线程B可能已经修改了主内存中value的值

  • 对于volatile修饰的变量,直接读写是没问题的, 能保证可见性

  • 但是对volatile变量进行操作, 原子性还是无法保证, 需要采用其他同步措施

    • 例如:synchronized

这里写图片描述

有序性

//线程1:
context = loadContext();   //语句1
loaded = true;             //语句2

//线程2:
while(!loaded ){
  sleep()
}
doSomethingwithconfig(context);
/*由于编译器的指令重排, 语句2有可能放到语句1的前边,  
这会导致线程2执行出错*/

例子:单例模式:看起来没什么问题

这里写图片描述

假设有两个线程,他们是怎么执行的?

例子:单例模式

这里写图片描述

例子:发生了指令重排

这里写图片描述

这里写图片描述

不允许对 一个volatile变量的赋值操作与其之前的任何读写操作 重新排序,
也不允许将 读取一个volatile变量的操作与其之后的任何读写操作 重新排序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值