文档章节

Atomic与Volatile对比

SuShine
 SuShine
发布于 2017/06/01 15:02
字数 1195
阅读 17
收藏 0
点赞 0
评论 0

java.util.concurrent.atomic.Atomic*原子类和volatile关键字是java中两种常见的处理多线程下数据共享读写的机制。二者看似相同,但是在实际应用中有着不小的差别。

volatile关键字是通过本地代码实现的写锁,只保证知有一个线程在写某个数据。JVM为了提高数据存取的速度,允许每个线程在自己独立的数据块,对进程中共享的数据进行私有拷贝。volatile就是保证每次读数据时,读的都是存在共享数据块里的数据,而不是私有拷贝。然而,这种机制在有些情况下并不安全。当两个线程T1,T2同时对volatitle int i;作i++;时,可能出现问题。i++相当于为i=i+1。

T1 LOAD i

T2 LOAD i

T1 STORE i+1

T2 STORE i+1

这里应该执行两次i=i+1,得到i=i+2的,但是结果确实i=i+1。

因此,这边就有了Atomic原子类存在的价值了。Atomic类被设计通过代码来解决这个问题, 是volatile的增强版。

以AtomicInteger为例,public class AtomicInteger extends Number implements java.io.Serializable。

它的父类Number没有一个Field,直接继承与Object,方法也只有abstract的int intValue(),long longValue(),float floatValue(),double doubleValue(),和通过intValue()实现的byte byteValue(),short shortValue()。简单的说,除了序列化外啥field都没有都没有。

AtomicInteger有四个field,除了序列化那个无视掉外,还剩三个。

    // setup to use Unsafe.compareAndSwapInt for updates

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

    private static final long valueOffset;

    static {

      try {

        valueOffset = unsafe.objectFieldOffset

            (AtomicInteger.class.getDeclaredField("value"));

      } catch (Exception ex) { throw new Error(ex); }

    }

    private volatile int value;

valueOffset只在这个静态块里被写过,从函数命名上猜测应该是获取这个类中名为value的field到这个object的头指针的偏移(offset)。这边用到了unsafe。

Unsafe是干什么的?sun.misc.Unsafe,未开源。这个名字让我想起了C#里的关键字unsafe。C#为了方便C++程序员转C#,方便使用COM组件,给出了不安全代码unsafe关键字。Java这个Unsafe,能的到一个field距离它Object头的偏移,对Java来说显然是不安全的操作,他给用户直接操作内存的可能。

除了objectFieldOffset(Field)外,这个类中还使用到Unsafe的putOrderedInt(Object,long,int)和compareAndSwapInt(Object,long,int,int)。从方法名和参数看,功能分别为向Object偏移long位置,写入有序整型int,和比较两值是否相同,并填入新值。

这边有篇IBM关于compareAndSwapInt的文档,我写得差不多了才看到,怨念。。。http://www.ibm.com/developerworks/cn/java/j-jtp11234/

顺带说一句,调用到compareAndSwapInt的两个方法compareAndSet和weakCompareAndSet的在1.6里源码是一样的,样的,的。。。冗余代码很好玩吗,API上写得各种看不懂。。。

剩下一个field就是private volatile int value了。也用了volatitle,由此可以得出,Atomic类在效率上是低于直接用volatitle,能使用volatitle是,就别用Atomic了。

然后看看AtomicInteger的method吧

从Number父类继承来的那几个可以看作getter函数的value类,清一色的调用了get()方法,然后转了一下类型。

 

    public final int get() {

        return value;

    }

直接把value扔回去了,可见Atomic类在处理读操作时和volatitle没啥区别。

看上去像setter函数的一共有五个:

 

    public final void set(int newValue) {

            value = newValue;

    }

     public final void lazySet(int newValue) {

              unsafe.putOrderedInt(this, valueOffset, newValue);

    }

    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);

    }

    public final boolean weakCompareAndSet(int expect, int update) {

              return unsafe.compareAndSwapInt(this, valueOffset, expect, update);

    }

weakCompareAndSet和compareAndSet实现上没有区别,所以无视掉吧,反正AtomicInteger自己也只用compareAndSet。。。

set(int)和直接用volatitle没啥区别,所以只用set作写差不多就和完全没有用到Atomic类一样,还多调用一次函数。

lazySet(int)调用了Unsafe.putOrderedInt,我们只能猜测是直接给内存把个数字写上去的。这样的话其实。。。我也没觉得有啥区别,就是告诉大家一下,你用Unsafe可以把Java当C++写嘛。。。

getAndSet(int)调用了compareAndSet(int,int),所作的基本就是不停的刷,看看自己改成了没有,改成了救过,没改成继续改。这边要出现两个线程不停对改怎么办。。。

最后一个compareAndSet(int,int)才是实现Atomic类功能的地方。就是比一下预想的和实际的是不是一样,一样就就用新值盖掉他,不一样就返回个false。怎么用呢,看看其他函数就是到了。

对比getAndIncrement()和incrementAndGet()

    public final int getAndIncrement() {

        for (;;) {

            int current = get();

            int next = current + 1;

            if (compareAndSet(current, next))

                return current;

        }

    }

 

    public final int incrementAndGet() {

        for (;;) {

            int current = get();

            int next = current + 1;

            if (compareAndSet(current, next))

                return next;

        }

    }

本文转载自:http://blog.csdn.net/zmx729618/article/details/52777132

共有 人打赏支持
SuShine
粉丝 118
博文 435
码字总数 88625
作品 0
青岛
后端工程师
PACKAGE java.util.concurrent.atomic

Package java.util.concurrent.atomic Description A small toolkit of classes that support lock-free thread-safe programming on single variables. In essence, the classes in this pa......

刘小兵2014 ⋅ 2011/07/28 ⋅ 0

Linux内核同步原子操作

避免对同一数据的并发访问(通常由中断、对称多处理器、内核抢占等引起)称为同步。 ——题记 内核源码:linux-2.6.38.8.tar.bz2 目标平台:ARM体系结构 原子操作确保对同一数据的“读取-修改...

有些服务器 ⋅ 2015/10/04 ⋅ 0

java.util.concurrent.atomic随笔及volatile语义

一个原子操作(atomic operation)是个不能分割的整体,没有其它线程(thread)能够中断或检查正在原子操作中的变量。一个原子(atomic)类型就是一个原子操作可用的类型,它可以在基本上没有...

刘小兵2014 ⋅ 2011/07/28 ⋅ 0

Java线程:原子量

所谓的原子量即操作变量的操作是“原子的”,该操作不可再分,因此是线程安全的。 为何要使用原子变量呢,原因是多个线程对单个变量操作也会引起一些问题。在Java5之前,可以通过volatile、s...

古月楼 ⋅ 2013/08/26 ⋅ 0

AtomicInteger的并发处理

jdk5之后的java.util.concurrent.atomic包里,多了一批原子处理类。主要用于在高并发环境下的高效程序处理。 这里,我们来看看AtomicInteger是如何使用非阻塞算法来实现并发控制的。 Atomic...

mrliuze ⋅ 2015/10/29 ⋅ 0

Java JUC之Atomic系列12大类实例讲解和原理分解

在java6以后我们不但接触到了Lock相关的锁,也接触到了很多更加乐观的原子修改操作,也就是在修改时我们只需要保证它的那个瞬间是安全的即可,经过相应的包装后可以再处理对象的并发修改,以...

owensliu ⋅ 2016/06/14 ⋅ 0

java.util.concurrent的AtomicInteger类详解

java.util.concurrent包下面有很多原子性的线程安全的数据结构的实现。研究一下常用的AtomicInteger类。 AtomicInteger类对应着常用的int类型,看一下它如何实现原子性的。 static块里面的看...

wangtx ⋅ 2016/05/11 ⋅ 0

【多线程】volatile非原子的特性

一、前言: 上一遍博客中已经提及了volatile关键字虽然拥有了多个线程之间的可见性,但是却不具备同步性(也就是原子性),可以算的上是线程同步的轻量级实现,性能要比synchronized强很多,...

qq_26545305 ⋅ 01/25 ⋅ 0

【并发编程】volatile非原子的特性

一、前言: 上一遍博客中已经提及了volatile关键字虽然拥有了多个线程之间的可见性,但是却不具备同步性(也就是原子性),可以算的上是线程同步的轻量级实现,性能要比synchronized强很多,...

qq_26545305 ⋅ 01/25 ⋅ 0

聊聊原子操作那些事

原子操作,线程间交互数据最细粒度的同步操作,它可以保证线程间读写某个数值的原子性。 由于不需要加重量级的互斥锁进行同步,因此非常轻量,而且也不需要在内核间来回切换调度,效率是非常...

ruki ⋅ 2016/10/01 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Day 17 vim简介与一般模式介绍

vim简介 vi和Vim的最大区别就是编辑一个文件时vi不会显示颜色,而Vim会显示颜色。显示颜色更便于用户编辑,凄然功能没有太大的区别 使用 yum install -y vim-enhanced 安装 vim的三种常用模式...

杉下 ⋅ 27分钟前 ⋅ 0

【每天一个JQuery特效】根据可见状态确定是否显示或隐藏元素(3)

效果图示: 主要代码: <!DOCTYPE html><html><head><meta charset="UTF-8"><title>根据可见状态确定 是否显示或隐藏元素</title><script src="js/jquery-3.3.1.min.js" ty......

Rhymo-Wu ⋅ 36分钟前 ⋅ 0

OSChina 周四乱弹 —— 初中我身体就已经垮了,不知道为什么

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @加油东溪少年 :下完这场雨 后弦 《下完这场雨》- 后弦 手机党少年们想听歌,请使劲儿戳(这里) @马丁的代码 :买了日本 日本果然赢了 翻了...

小小编辑 ⋅ 55分钟前 ⋅ 8

浅谈springboot Web模式下的线程安全问题

我们在@RestController下,一般都是@AutoWired一些Service,由于这些Service都是单例,所以并不存在线程安全问题。 由于Controller本身是单例模式 (非线程安全的), 这意味着每个request过来,...

算法之名 ⋅ 今天 ⋅ 0

知乎Java数据结构

作者:匿名用户 链接:https://www.zhihu.com/question/35947829/answer/66113038 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 感觉知乎上嘲讽题主简...

颖伙虫 ⋅ 今天 ⋅ 0

Confluence 6 恢复一个站点有关使用站点导出为备份的说明

推荐使用生产备份策略。我们推荐你针对你的生产环境中使用的 Confluence 参考 Production Backup Strategy 页面中的内容进行备份和恢复(这个需要你备份你的数据库和 home 目录)。XML 导出备...

honeymose ⋅ 今天 ⋅ 0

JavaScript零基础入门——(九)JavaScript的函数

JavaScript零基础入门——(九)JavaScript的函数 欢迎回到我们的JavaScript零基础入门,上一节课我们了解了有关JS中数组的相关知识点,不知道大家有没有自己去敲一敲,消化一下?这一节课,...

JandenMa ⋅ 今天 ⋅ 0

火狐浏览器各版本下载及插件httprequest

各版本下载地址:http://ftp.mozilla.org/pub/mozilla.org//firefox/releases/ httprequest插件截至57版本可用

xiaoge2016 ⋅ 今天 ⋅ 0

Docker系列教程28-实战:使用Docker Compose运行ELK

原文:http://www.itmuch.com/docker/28-docker-compose-in-action-elk/,转载请说明出处。 ElasticSearch【存储】 Logtash【日志聚合器】 Kibana【界面】 答案: version: '2'services: ...

周立_ITMuch ⋅ 今天 ⋅ 0

使用快嘉sdkg极速搭建接口模拟系统

在具体项目研发过程中,一旦前后端双方约定好接口,前端和app同事就会希望后台同事可以尽快提供可供对接的接口方便调试,而对后台同事来说定好接口还仅是个开始、设计流程,实现业务逻辑,编...

fastjrun ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部