我们知道在并发编程中,多个线程共享某个变量或者对象时,必须要进行同步。同步的包含两层作用:1)互斥访问(原子性);2)可见性;也就是多个线程对共享的变量互斥地访问,同时线程对共享变量的修改必须对其他线程可见,也就是所有线程访问到的都是最新的值。

1. volatile变量和volatile引用

volatile的作用是:保证可见性,但是没有互斥访问语义(原子性语义)。volatile能够保证它修饰的引用以及引用的对象的可见性,volatile不仅保证变量或者引用对所有访问它的线程的可见性,同时能够保证它所引用的对象对所有访问它的线程的可见性。volatile的使用要求满足下面的两个条件:

1)对变量或者引用的写操作不依赖于变量或者引用的当前值(如果只有特定的单个线程修改共享变量,那么修改操作也是可以依赖于当前值);

2)该变量或者引用没有包含在其它的不变式条件中;

volatile最常见的错误使用场景是使用volatile来实现并发 i++; 错误的原因是,该操作依赖于 i 变量的当前值,他是在 i 变量的当前值的基础上加一,所以说他依赖于 i 的当前值。多个线程执行 i++; 会丢失更新。比如两个线程同时读到 i 的当前值8,都进行加一,然后写回,最终 i 的结果是 9,而不是我们期待的10,丢失了更新。那么原子变量的引入就是针对volatile的这个缺陷的!!!原子变量的修改操作允许它依赖于当前值,所以说”原子变量“是比volatile的语义稍微强化一点!他不仅具有volatile的可见性,同时对原子变量的修改可以依赖于当前值。

2. 原子变量和原子引用

从Java 1.5开始引入了原子变量和原子引用:

java.util.concurrent.atomic.AtomicBoolean

java.util.concurrent.atomic.AtomicInteger

java.util.concurrent.atomic.AtomicLong

java.util.concurrent.atomic.AtomicReference

以及他们对应的数组:

java.util.concurrent.atomic.AtomicIntegerArray

java.util.concurrent.atomic.AtomicLongArray

java.util.concurrent.atomic.AtomicReferenceArray

原子变量和引用都是使用compareAndSwap(CAS指令)来实现:依赖当前值的原子修改的。

而且他们的实现都是使用volatile和Unsafe:volatile保证可见性,而Unsafe保证原子性

我们可以稍微分析下AtomicReference的源码:

public class AtomicReference<V> implements java.io.Serializable {
private static final long serialVersionUID = -1848883965231344442L; private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset; static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicReference.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
} private volatile V value; /**
* Creates a new AtomicReference with the given initial value.
*
* @param initialValue the initial value
*/
public AtomicReference(V initialValue) {
value = initialValue;
} /**
* Creates a new AtomicReference with null initial value.
*/
public AtomicReference() {
} /**
* Gets the current value.
*
* @return the current value
*/
public final V get() {
return value;
} /**
* Sets to the given value.
*
* @param newValue the new value
*/
public final void set(V newValue) {
value = newValue;
} /**
* Eventually sets to the given value.
*
* @param newValue the new value
* @since 1.6
*/
public final void lazySet(V newValue) {
unsafe.putOrderedObject(this, valueOffset, newValue);
} /**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(V expect, V update) {
return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
} /**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* <p><a href="package-summary.html#weakCompareAndSet">May fail
* spuriously and does not provide ordering guarantees</a>, so is
* only rarely an appropriate alternative to {@code compareAndSet}.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful
*/
public final boolean weakCompareAndSet(V expect, V update) {
return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
} /**
* Atomically sets to the given value and returns the old value.
*
* @param newValue the new value
* @return the previous value
*/
@SuppressWarnings("unchecked")
public final V getAndSet(V newValue) {
return (V)unsafe.getAndSetObject(this, valueOffset, newValue);
} /**
* Atomically updates the current value with the results of
* applying the given function, returning the previous value. The
* function should be side-effect-free, since it may be re-applied
* when attempted updates fail due to contention among threads.
*
* @param updateFunction a side-effect-free function
* @return the previous value
* @since 1.8
*/
public final V getAndUpdate(UnaryOperator<V> updateFunction) {
V prev, next;
do {
prev = get();
next = updateFunction.apply(prev);
} while (!compareAndSet(prev, next));
return prev;
}
......

我们可以看到使用了: private volatile V value; 来保证 value 的可见性;

同时:

private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicReference.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

这段代码的意思是:获得AtomicReference<V>实例化对象中的 value 属性的在该对象在堆内存的偏移 valueOffset 位置,而:

unsafe.compareAndSwapObject(this, valueOffset, expect, update);

的作用就是通过比较valueOffset处的内存的值是否为expect,是的话就更新替换成新值update,这个操作是原子性的。所以volatile保证了可见性,而unsafe保证了原子性。源码中的UnaryOperator,BinaryOperator等等明显是模仿C++的,因为Java中没有函数指针,所以只能使用一元、二元操作对象来实现相应的功能。

3. Unsafe

Unsafe的源码可以参见:http://www.docjar.com/html/api/sun/misc/Unsafe.java.html

他的实现主要是通过编译器,利用CPU的一些原子指令来实现的。

Most methods in this class are very low-level, and correspond to a
small number of hardware instructions (on typical machines). Compilers
are encouraged to optimize these methods accordingly.

4. LongAdder(加法器)

在jdk 1.8中又引入了进过充分优化的原子变量“加法器”:java.util.concurrent.atomic.LongAdder,它的性能在和其他原子变量以及volatile变量相比都是最好的,所以在能使用LongAdder的地方就不要使用其它原子变量了。但是LongAdder中并没有提供:依赖于当前变量的值来修改的操作。一般用于实现并发计数器是最好的。

LongAdder实现下列接口:

public void add(long x);    // 加 x
public void increment(); // 加1
public void decrement(); // 减1
public long sum(); // 求和
public void reset(); // 重置0
public String toString() {
return Long.toString(sum());
} /**
* Equivalent to {@link #sum}.
*
* @return the sum
*/
public long longValue() {
return sum();
} /**
* Returns the {@link #sum} as an {@code int} after a narrowing
* primitive conversion.
*/
public int intValue() {
return (int)sum();
} /**
* Returns the {@link #sum} as a {@code float}
* after a widening primitive conversion.
*/
public float floatValue() {
return (float)sum();
} /**
* Returns the {@link #sum} as a {@code double} after a widening
* primitive conversion.
*/
public double doubleValue() {
return (double)sum();
}

总结:

就同步语义而言,volatile < 原子变量/原子引用 < lock/synchronized. volatile只保证可见性;原子变量和原子引用保证可见性的同时,利用CAS指令实现了原子修改——“修改操作允许它依赖于当前值”;而lock/synchronized则同时保证可见性和互斥性(原子性)。

Java并发之原子变量和原子引用与volatile的更多相关文章

  1. linux内核原子变量与原子位操作API

    原子变量: arch/arm/include/asm/atomic.h 定义并初始化 atomic_t v = ATOMIC_INIT(0); 写 void atomic_set(atomic_t * ...

  2. java并发之可见性与原子性:Syncronized和volatile

    转载:http://blog.csdn.net/guyuealian/article/details/52525724 在说明Java多线程内存可见性之前,先来简单了解一下Java内存模型.     ...

  3. java并发编程实战:第十五章----原子变量与非阻塞机制

    非阻塞算法:使用底层的原子机器指令(例如比较并交换指令)代替锁来确保数据在并发访问中的一致性 应用于在操作系统和JVM中实现线程 / 进程调度机制.垃圾回收机制以及锁和其他并发数据结构 可伸缩性和活跃 ...

  4. Java锁与非阻塞算法的性能比较与分析+原子变量类的应用

    15.原子变量与非阻塞同步机制 在java.util.concurrent包中的许多类,比如Semaphore和ConcurrentLinkedQueue,都提供了比使用Synchronized更好的 ...

  5. Java 理论与实践: 流行的原子——新原子类是 java.util.concurrent 的隐藏精华(转载)

    简介: 在 JDK 5.0 之前,如果不使用本机代码,就不能用 Java 语言编写无等待.无锁定的算法.在 java.util.concurrent 中添加原子变量类之后,这种情况发生了变化.请跟随并 ...

  6. Java 理论与实践: 流行的原子

    Java 理论与实践: 流行的原子 新原子类是 java.util.concurrent 的隐藏精华 在 JDK 5.0 之前,如果不使用本机代码,就不能用 Java 语言编写无等待.无锁定的算法.在 ...

  7. 原子变量与CAS算法

    原子变量 为了引出原子变量这个概念,我们先看一个例子. package com.ccfdod.juc; public class TestAtomicDemo { public static void ...

  8. 【JUC系列第二篇】-原子变量

    作者:毕来生 微信:878799579 1.什么是原子变量? ​ 原子变量保证了该变量的所有操作都是原子的,不会因为多线程的同时访问而导致脏数据的读取问题. 2.通过synchronized保证原子操 ...

  9. Java编程的逻辑 (70) - 原子变量和CAS

    ​本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http: ...

随机推荐

  1. iOS开发中静态库制作 之.a静态库制作及使用篇

    iOS开发中静态库之".a静态库"的制作及使用篇 一.库的简介 1.什么是库? 库是程序代码的集合,是共享程序代码的一种方式 2.库的类型? 根据源代码的公开情况,库可以分为2种类 ...

  2. Sql Server系列:SQL语句查询数据库中表、视图、存储过程等组成

    1. 查看用户表 select name from sys.tables select name from sys.objects where type='U' select name from sy ...

  3. 前端学HTTP之代理

    前面的话 Web代理(proxy)服务器是网络的中间实体,位于客户端和服务器之间,扮演“中间人”的角色,在各端点之间来回传送HTTP报文.本文将介绍HTTP代理服务器相关内容 中间实体 Web上的代理 ...

  4. 高仿QQ顶部控件之IOS SegmentView

    经常会看到QQ上面有一个 消息和电话 的顶部面板,这个空间是IOS7的分段控制,android中没有这个控件,今天在威哥的微信公众号中成功gank到这个自定义控件的实现,下面跟着尝试一波. 首先是定义 ...

  5. 现代3D图形编程学习-关于本书(译)

    本书系列 现代3D图形编程学习 关于这本书 三维图像处理硬件很快成为了必不可少的组件.很多操作系统能够直接使用三维图像硬件,有些甚至要求需要有3D渲染能力的硬件.同时对于日益增加的手机系统,3D图像硬 ...

  6. Unity3D中使用委托和事件

    前言: 本来早就想写写和代码设计相关的东西了,以前做2DX的时候就有过写写观察者设计模式的想法,但是实践不多.现在转到U3D的怀抱中,倒是接触了不少委托事件的写法,那干脆就在此总结一下吧. 1.C#中 ...

  7. SQL Server里在文件组间如何移动数据?

    平常我不知道被问了几次这样的问题:“SQL  Server里在文件组间如何移动数据?“你意识到这个问题:你只有一个主文件组的默认配置,后来围观了“SQL Server里的文件和文件组”后,你知道,有多 ...

  8. JSON扩展类——JsonHelper

    1.引用Newtonsoft.Json库(JSON.NET). 2.复制粘贴JsonHelper吧. 源代码: using System; using System.Collections.Gener ...

  9. Navisworks Api Quantification

    Quantification  国外有的叫定量  我们国内一些施工方叫工程量. 通过TakeOff API的开发者有机会获得更多的数据和数据可通过图形用户界面. 1 添加Navisworks的Api ...

  10. 几道web前端练习题目

    在 HTML 语言中,以下哪个属性不是通用属性?A]<class>B]<title>C]<href>D]<style> 在线练习:http://hove ...