Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 855|回复: 0

Java多核线程笔记---volatile的原理与技巧

[复制链接]

该用户从未签到

发表于 2011-9-14 20:20:21 | 显示全部楼层 |阅读模式
为什么使用volatile比同步代价更低?  同步的代价,主要由其覆盖范围决定,如果可以降低同步的覆盖范围,则可以大幅提升程序性能。而volatile的覆盖范围仅仅变量级别的。因此它的同步代价很低。volatile原理是什么?  volatile的语义,其实是告诉处理器,不要将我放入工作内存(工作内存详见java内存模型),请直接在主存操作我。因此,当多核或多线程在访问该变量时,都将直接操作主存,这从本质上,做到了变量共享。volatile有什么优势?  1,更大的程序吞吐量  2,更少的代码实现多线程  3,程序的伸缩性较好  4,比较好理解,无需太高的学习成本volatile有什么劣势?  1,容易出问题  2,比较难设计
volatile运算存在脏数据问题volatile仅仅能保证变量可见性,无法保证原子性。volatile的race condition示例:
public class TestRaceCondition {
   private volatile int i = 0;

   public void increase() {
      i++;
   }

   public int getValue() {
      return i;
   }
}
当多线程执行increase方法时,是否能保证它的值会是线性递增的呢?   答案是否定的。  原因:  这里的increase方法,执行的操作是i++,即 i = i + 1; 针对i = i + 1,在多线程中的运算,本身需要改变i的值。如果,在i已从内存中取到最新值,但未与1进行运算,此时其他线程已数次将运算结果赋值给i。则当前线程结束时,之前的数次运算结果都将被覆盖。即,执行100次increase,可能结果是 < 100。  一般来说,这种情况需要较高的压力与并发情况下,才会出现。如何避免这种情况?  解决以上问题的方法:  一种是操作时,加上同步。  这种方法,无疑将大大降低程序性能,且违背了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 1.5 并发包 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 (compareAndSet(current, newValue))
         return current;
   }
}

public final boolean compareAndSet(int expect, int update) {
   return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
  这个方法是AtomicInteger类的常用方法,作用是将变量设置为指定值,并返回设置前的值。它利用了cpu原语compareAndSet来保障值的唯一性。  另,AtomicInteger类中,其他的实用方法,也是基于同样的实现方式。比如 getAndIncrement,getAndDecrement,getAndAdd等等。
CAS语义上存在的“ABA 问题”什么是ABA问题?  假设,第一次读取V地址的A值,然后通过CAS来判断V地址的值是否仍旧为A,如果是,就将B的值写入V地址,覆盖A值。但是,语义上,有一个漏洞,当第一次读取V的A值,此时,内存V的值变为B值,然后在未执行CAS前,又变回了A值。此时,CAS再执行时,会判断其正确的,并进行赋值。  这种判断值的方式来断定内存是否被修改过,针对某些问题,是不适用的。  为了解决这种问题,jdk 1.5并发包提供了 AtomicStampedReference(有标记的原子引用)类,通过控制变量值的版本来保证CAS正确性。其实,大部分通过值的变化来CAS,已经够用了。
jdk1.5原子包介绍(基于volatile)
包的特色:
  1,普通原子数值类型AtomicInteger,AtomicLong提供一些原子操作的加减运算。
  2,使用了解决脏数据问题的经典模式-“比对后设定”,即 查看主存中数据是否与预期提供的值一致,如果一致,才更新。
  3,使用AtomicReference可以实现对所有对象的原子引用及赋值。包括Double与Float,但不包括对其的计算。浮点的计算,只能依靠同步关键字或Lock接口来实现了。
  4,对数组元素里的对象,符合以上特点的,也可采用原子操作。包里提供了一些数组原子操作类AtomicIntegerArray,AtomicLongArray等等。
  5,大幅度提升系统吞吐量及性能。
  具体使用,详解java doc。
回复

使用道具 举报

该用户从未签到

发表于 2011-9-28 12:59:56 | 显示全部楼层
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

GMT+8, 2024-5-16 02:23 , Processed in 0.441071 second(s), 46 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表