知识大全 多核线程-volatile原理与技巧
Posted 操作
篇首语:越工作越能工作,越忙碌越能发明出闲暇。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 多核线程-volatile原理与技巧相关的知识,希望对你有一定的参考价值。
为什么使用volatile比同步代价更低?
同步的代价 主要由其覆蓋范围决定 如果可以降低同步的覆蓋范围 则可以大幅提升程序性能
而volatile的覆蓋范围仅仅变量级别的 因此它的同步代价很低
volatile原理是什么?
volatile的语义 其实是告诉处理器 不要将我放入工作内存 请直接在主存操作我 (工作内存详见java内存模型)
因此 当多核或多线程在访问该变量时 都将直接操作主存 这从本质上 做到了变量共享
volatile的有什么优势?
更大的程序吞吐量
更少的代码实现多线程
程序的伸缩性较好
比较好理解 无需太高的学习成本
volatile有什么劣势?
容易出问题
比较难设计
volatile运算存在脏数据问题
volatile仅仅能保证变量可见性 无法保证原子性
volatile的race condition示例
public class TestRaceCondition private volatile int i = ; public void increase() i++; public int getValue() return i;
当多线程执行increase方法时 是否能保证它的值会是线性递增的呢?
答案是否定的
原因
这里的increase方法 执行的操作是i++ 即 i = i +
针对i = i + 在多线程中的运算 本身需要改变i的值
如果 在i已从内存中取到最新值 但未与 进行运算 此时其他线程已数次将运算结果赋值给i
则当前线程结束时 之前的数次运算结果都将被覆蓋
即 执行 次increase 可能结果是 <
一般来说 这种情况需要较高的压力与并发情况下 才会出现
如何避免这种情况?
解决以上问题的方法
一种是 操作时 加上同步
这种方法 无疑将大大降低程序性能 且违背了volatile的初衷
第二种方式是 使用硬件原语(CAS) 实现非阻塞算法
从CPU原语上 支持变量级别的低开销同步
CPU原语 比较并交换(CompareAndSet) 实现非阻塞算法
什么是CAS?
cas是现代CPU提供给并发程序使用的原语操作 不同的CPU有不同的使用规范
在 Intel 处理器中 比较并交换通过指令的 cmpxchg 系列实现
PowerPC 处理器有一对名为 加载并保留 和 条件存储 的指令 它们实现相同的目地
MIPS 与 PowerPC 处理器相似 除了第一个指令称为 加载链接
CAS 操作包含三个操作数 —— 内存位置(V) 预期原值(A)和新值(B)
什么是非阻塞算法?
一个线程的失败或挂起不应该影响其他线程的失败或挂起 这类算法称之为非阻塞(nonblocking)算法
对比阻塞算法
如果有一类并发操作 其中一个线程优先得到对象监视器的锁 当其他线程到达同步边界时 就会被阻塞
直到前一个线程释放掉锁后 才可以继续竞争对象锁 (当然 这里的竞争也可是公平的 按先来后到的次序)
CAS 原理
我认为位置 V 应该包含值 A 如果包含该值 则将 B 放到这个位置 否则 不要更改该位置 只告诉我这个位置现在的值即可
CAS使用示例(jdk 并发包 AtomicInteger类分析
/** * Atomically sets to the given value and returns the old value * * @param newValue the new value * @return the previous value */ public final int getAndSet(int newValue) for (;;) int current = get(); if (pareAndSet(current newValue)) return current; public final boolean pareAndSet(int expect int update) return pareAndSwapInt(this valueOffset expect update);
这个方法是 AtomicInteger类的常用方法 作用是 将变量设置为指定值 并返回设置前的值
它利用了cpu原语pareAndSet来保障值的唯一性
另 AtomicInteger类中 其他的实用方法 也是基于同样的实现方式
比如 getAndIncrement getAndDecrement getAndAdd等等
CAS语义上存在的 ABA 问题
什么是ABA问题?
假设 第一次读取V地址的A值 然后通过CAS来判断V地址的值是否仍旧为A 如果是 就将B的值写入V地址 覆蓋A值
但是 语义上 有一个漏洞 当第一次读取V的A值 此时 内存V的值变为B值 然后在未执行CAS前 又变回了A值
此时 CAS再执行时 会判断其正确的 并进行赋值
这种判断值的方式来断定内存是否被修改过 针对某些问题 是不适用的
为了解决这种问题 jdk 并发包提供了AtomicStampedReference(有标记的原子引用)类 通过控制变量值的版本来保证CAS正确性
其实 大部分通过值的变化来CAS 已经够用了
jdk 原子包介绍(基于volatile)
包的特色
普通原子数值类型AtomicInteger AtomicLong提供一些原子操作的加减运算
使用了解决脏数据问题的经典模式 比对后设定 即 查看主存中数据是否与预期提供的值一致 如果一致 才更新
使用AtomicReference可以实现对所有对象的原子引用及赋值 包括Double与Float
但不包括对其的计算 浮点的计算 只能依靠同步关键字或Lock接口来实现了
对数组元素里的对象 符合以上特点的 也可采用原子操作 包里提供了一些数组原子操作类
AtomicIntegerArray AtomicLongArray等等
cha138/Article/program/Java/gj/201311/27580相关参考
知识大全 JAVA高级:多核线程-volatile原理与技巧[4]
JAVA高级:多核线程-volatile原理与技巧[4] 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下
知识大全 JAVA高级:多核线程-volatile原理与技巧[3]
JAVA高级:多核线程-volatile原理与技巧[3] 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下
知识大全 Java 理论与实践: 正确使用 volatile 变量 线程同步
Java理论与实践:正确使用volatile变量线程同步 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下
Java语言规范中指出为了获得最佳速度允许线程保存共享成员变量的私有拷贝而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比 Volatile修饰的成员变量在每次被线程访问时都强迫从
Java多线程之volatile深入分析 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! vola
以前一直没在意一直以为volatile修饰了就高枕无忧了但其实不然最简单的一个场景 publicclassCounter publicvolatilestaticintcount=; pub
Java多线程初学者指南(6):慎重使用volatile关键字 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来
Java线程的概念与原理 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 一操作系统中线程和进程的
知识大全 Java理论与实践: 正确使用Volatile变量
Java理论与实践:正确使用Volatile变量 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!