关闭

Java并发编程系列之十九:原子操作类

标签: 并发编程
7997人阅读 评论(0) 收藏 举报
分类:

原子操作类简介

当更新一个变量的时候,多出现数据争用的时候可能出现所意想不到的情况。这时的一般策略是使用synchronized解决,因为synchronized能够保证多个线程不会同时更新该变量。然而,从jdk 5之后,提供了粒度更细、量级更轻,并且在多核处理器具有高性能的原子操作类。因为原子操作类把竞争的范围缩小到单个变量上,这可以算是粒度最细的情况了。

原子操作类相当于泛化的volatile变量,能够支持原子读取-修改-写操作。比如AtomicInteger表示一个int类型的数值,提供了get和set方法,这些volatile类型的变量在读取与写入上有着相同的内存语义。原子操作类共有13个类,在java.util.concurrent.atomic包下,可以分为四种类型的原子更新类:原子更新基本类型、原子更新数组类型、原子更新引用和原子更新属性。

下面将分别介绍这四种原子操作类。

原子更新基本类型

使用原子方式更新基本类型,共包括3个类:

  • AtomicBoolean:原子更新布尔变量
  • AtomicInteger:原子更新整型变量
  • AtomicLong:原子更新长整型变量

具体到每个类的源代码中,提供的方法基本相同,这里以AtomicInteger为例进行说明。AtomicInteger提供的部分方法如下:

public class AtomicInteger extends Number implements java.io.Serializable {
    //返回当前的值
    public final int get() {
        return value;
    }
    //原子更新为新值并返回旧值
    public final int getAndSet(int newValue) {
        return unsafe.getAndSetInt(this, valueOffset, newValue);
    }
    //最终会设置成新值
    public final void lazySet(int newValue) {
        unsafe.putOrderedInt(this, valueOffset, newValue);
    }
    //如果输入的值等于预期值,则以原子方式更新为新值
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
    //原子自增
    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }
    //原子方式将当前值与输入值相加并返回结果
    public final int getAndAdd(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta);
    }
}

为了说明AtomicInteger的原子性,这里代码演示多线程对一个int值进行自增操作,最后输出结果,代码如下:

package com.rhwayfun.concurrency.r0405;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by rhwayfun on 16-4-5.
 */
public class AtomicIntegerDemo {

    private static AtomicInteger atomicInteger = new AtomicInteger(0);

    public static void main(String[] args){
        for (int i = 0; i < 5; i++){
            new Thread(new Runnable() {
                public void run() {
                    //调用AtomicInteger的getAndIncement返回的是增加之前的值
                    System.out.println(atomicInteger.getAndIncrement());
                }
            }).start();
        }
        System.out.println(atomicInteger.get());
    }
}

输出结果如下:

0
1
2
3
4
5
5

可以看到在多线程的情况下,得到的结果是正确的,但是如果仅仅使用int类型的成员变量则可能得到不同的结果。这里的关键在于getAndIncrement是原子操作,那么是如何保证的呢?

getAndIncrement方法的源码如下:

    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

    public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

    public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

到这里可以发现最终调用了native方法来保证更新的原子性。

原子更新数组

通过原子更新数组里的某个元素,共有3个类:

  • AtomicIntegerArray:原子更新整型数组的某个元素
  • AtomicLongArray:原子更新长整型数组的某个元素
  • AtomicReferenceArray:原子更新引用类型数组的某个元素

AtomicIntegerArray常用的方法有:

  • int addAndSet(int i, int delta):以原子方式将输入值与数组中索引为i的元素相加
  • boolean compareAndSet(int i, int expect, int update):如果当前值等于预期值,则以原子方式更新数组中索引为i的值为update值

示例代码如下:

    package com.rhwayfun.concurrency.r0405;

    import java.util.concurrent.atomic.AtomicIntegerArray;

    /**
     * Created by rhwayfun on 16-4-5.
     */
    public class AtomicIntegerArrayDemo {

    static int[] value = new int[]{1, 2};

    static AtomicIntegerArray ai = new AtomicIntegerArray(value);

    public static void main(String[] args){
        ai.getAndSet(0,3);
        System.out.println(ai.get(0));
        System.out.println(value[0]);
    }
}

运行结果是:

3
1

数组value通过构造的方式传入AtomicIntegerArray中,实际上AtomicIntegerArray会将当前数组拷贝一份,所以在数组拷贝的操作不影响原数组的值。

原子更新引用类型

需要更新引用类型往往涉及多个变量,早atomic包有三个类:

  • AtomicReference:原子更新引用类型
  • AtomicReferenceFieldUpdater:原子更新引用类型里的字段
  • AtomicMarkableReference:原子更新带有标记位的引用类型。

下面以AtomicReference为例进行说明:

package com.rhwayfun.concurrency.r0405;

import java.util.concurrent.atomic.AtomicReference;

/**
 * Created by rhwayfun on 16-4-5.
 */
public class AtomicReferenceDemo {

    static class User{
        private String name;
        private int id;

        public User(String name, int id) {
            this.name = name;
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }
    }

    public static AtomicReference<User> ar = new AtomicReference<User>();

    public static void main(String[] args){
        User user = new User("aa",11);
        ar.set(user);
        User newUser = new User("bb",22);
        ar.compareAndSet(user,newUser);
        System.out.println(ar.get().getName());
        System.out.println(ar.get().getId());
    }
}

运行结果为:

bb
22

可以看到user被成功更新。

原子更新字段类

如果需要原子更新某个类的某个字段,就需要用到原子更新字段类,可以使用以下几个类:

  • AtomicIntegerFieldUpdater:原子更新整型字段
  • AtomicLongFieldUpdater:原子更新长整型字段
  • AtomicStampedReference:原子更新带有版本号的引用类型。

要想原子更新字段,需要两个步骤:

  1. 每次必须使用newUpdater创建一个更新器,并且需要设置想要更新的类的字段
  2. 更新类的字段(属性)必须为public volatile

下面的代码演示如何使用原子更新字段类更新字段:

package com.rhwayfun.concurrency.r0405;

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

/**
 * Created by rhwayfun on 16-4-5.
 */
public class AtomicIntegerFieldUpdaterDemo {

    //创建一个原子更新器
    private static AtomicIntegerFieldUpdater<User> atomicIntegerFieldUpdater =
            AtomicIntegerFieldUpdater.newUpdater(User.class,"old");

    public static void main(String[] args){
        User user = new User("Tom",15);
        //原来的年龄
        System.out.println(atomicIntegerFieldUpdater.getAndIncrement(user));
        //现在的年龄
        System.out.println(atomicIntegerFieldUpdater.get(user));
    }

    static class User{
        private String name;
        public volatile int old;

        public User(String name, int old) {
            this.name = name;
            this.old = old;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getOld() {
            return old;
        }

        public void setOld(int old) {
            this.old = old;
        }
    }
}

输出的结果如下:

15
16

至此,我们知道了如何使用原子操作类在不同场景下的基本用法。

0
0
查看评论

Java 原子操作

-- Start java.util.concurrent.atomic 包中提供了以下原子类, 它们是线程安全的类, 但是它们并不是通过同步和锁来实现的, 原子变量的操作会变为平台提供的用于并发访问的硬件原语. AtomicBoolean -- 原子布尔AtomicInteger ...
  • shangboerds
  • shangboerds
  • 2012-07-16 16:27
  • 14751

Java多线程并发锁和原子操作,你真的了解吗?

对于Java多线程,接触最多的莫过于使用synchronized,这个简单易懂,可谓一招吃遍天下,可能在一些特殊场景下确需要用一些其他的同步进行。今天我就简单介绍一下几种锁,其实像类似的博客和文章和很多,所以也不会多赘述,我更想抛出在实际应用中所碰到的问题。好,废话少说。
  • luohuacanyue
  • luohuacanyue
  • 2012-07-28 11:47
  • 31098

Java i++是原子操作的假象

说来搞笑,昨天被面试宝典给彻底虐了。毁三观啊。如果说C++里面的运算符重载我知道,也知道尽量使用++i,因为这个我们都看得见,摸得着,所以很容易理解,只是Java这又是怎么回事呢? 吃饭时候想起之前面试的时候,面试官特别的gentle,和我讨论了很多web开发时候的问题,瞬间想到i++是不是在Jav...
  • Enbandarix
  • Enbandarix
  • 2014-03-02 17:07
  • 3289

java并发中的原子变量和原子操作以及CAS介绍

起源于一道面试题i++是不是原子操作?什么事原子操作?于是乎搜集答案:       很多情况下我们只是需要一个简单的、高效的、线程安全的递增递减方案。注意,这里有三个条件:简单,意味着程序员尽可能少的操作底层或者实现起来要比较容易;高效意味着耗用资源要少,...
  • wxw520zdh
  • wxw520zdh
  • 2016-12-18 23:11
  • 1685

深入浅出java原子操作

原文地址:深入浅出java原子操作作者:opaljc 转自http://www.blogjava.net/xylz/archive/2010/07/04/325206.html part1 从AtomicInteger开始   从相对简单的Atomic入手(java.u...
  • zhuzhiyunzzy
  • zhuzhiyunzzy
  • 2014-04-08 15:16
  • 1506

Java实现原子操作的原理

原子的定义: 原子(atomic)本意是"不能被进一步分割的最小粒子”,而原子操作描述为:“不可被中断的一个或一系列操作“。在多核处理器上实现原子操作就会变得复杂了许多。 原子操作的实现: 1.术语定义 术语名称      英文 解释 缓存行 Cache li...
  • liaodehong
  • liaodehong
  • 2016-07-18 21:01
  • 2270

java原子操作与并发

原子操作是JVM单位能处理的指令,在汇编中比如ADD操作 ,J跳转操作等。 我们的常用语句基本上都不是原子指令比如,A=A+1,这样的指令分两步,1A+1    2 赋值 这样的操作过程如果不注意,会在并发中引起巨大的问题。 下面是来自http://88...
  • u010953266
  • u010953266
  • 2015-01-29 22:26
  • 449

java中的原子操作类AtomicInteger及其实现原理

/**          * 一,AtomicInteger 是如何实现原子操作的呢?          *        &#...
  • reggergdsg
  • reggergdsg
  • 2016-07-05 22:40
  • 9641

Java原子操作AtomicInteger的用法

原文:http://www.jianshu.com/p/509aca840f6d 前言: JDK1.5之后的java.util.concurrent.atomic包里,多了一批原子处理类。AtomicBoolean、AtomicInteger、AtomicLong、AtomicRef...
  • u013063153
  • u013063153
  • 2017-04-21 17:48
  • 999

Java中的原子操作类

概述java.util.concurrent.atomic包一共提供了13个类,属于4中类型的原子更新方式:原子更新基本数据类型、原子更新数组、原子更新引用、原子更新属性。原子更新基本类型 >java.util.concurrent.atomic包提供了以下3个类: AtomicB...
  • fjse51
  • fjse51
  • 2017-02-24 15:37
  • 673
    个人资料
    • 访问:481538次
    • 积分:6907
    • 等级:
    • 排名:第4007名
    • 原创:217篇
    • 转载:13篇
    • 译文:1篇
    • 评论:265条
    博客专栏
    最新评论