java(十五):concurrent(0)—AtomicInteger,Unsafe,CAS

因为java在concurrent包中大量的使用到了AtomicInteger,于是打算从AtomicInteger开始对concurrent包的各个类做一番探究。
如果想对int型数据进行原子操作,那么推荐使用AtomicInteger。
当然也可以使用synchronized代码块,但使用AtomicInteger更高效。
如下代码说明了使用AtomicInteger和使用Integer的区别:

static AtomicInteger i = new AtomicInteger(0);
public static void main(String[] args) {
    Runnable[] threads = new Runnable[300];
    for(int j=0;j<300;j++){
        threads[j] = new Runnable() {
            public void run() {
                System.out.println(i.incrementAndGet());
            }
        };
    }
    for(int j=0;j<300;j++){
        new Thread(threads[j]).start();
    }
}

上述的代码最后输出结果中最大为300.
再看下面的代码:

static Integer i = new Integer(0);
public static void main(String[] args) {
    Runnable[] threads = new Runnable[300];
    for(int j=0;j<300;j++){
        threads[j] = new Runnable() {
            public void run() {
                System.out.println(++i);
            }
        };
    }
    for(int j=0;j<300;j++){
        new Thread(threads[j]).start();
    }
}

毫无疑问,将AtomicInteger换成Integer后,多线程没有进行同步,因此一般来说最后的结果中不会有300.
查看AtomicInteger的源码发现,其实同步多线程的操作是通过Unsafe对象来实现的。
于是来先来看看Unsafe吧。

Unsafe

Unsafe类提供了硬件级别的原子操作

对于不想细究的人来说,只需要记住一上一点就行了。即Unsafe提供了和操作系统、内存层面的操作,Unsafe类里面包含大量的native方法,其实就是将这些方法封装为Unsafe类,表示这些方法是底层实现的。

CAS

说道Unsafe,最著名的就是CAS操作,即Compare And Swap,比较并交换。
希望大家不要被这个词误导,CAS真的就是这么简单的意思,硬要在深挖一层的话那就是其实现的细节了。CAS使用Unsafe实现,具体来说是调用native方法,最常用的CAS方法如下:

public final class Unsafe {
    //...
    public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

var1是比较值所属的对象,var2需要比较的值(但实际是使用地址偏移量来实现的),如果var1对象中偏移量为var2处的值等于var4,那么将该处的值设置为var5并返回true,如果不等于var4则返回false。
简单来说这就是CAS的全部了,作为使用者,记住这些就OK.

park

还包括unpark方法。

Unsafe提供了park方法和unpark方法用于挂起线程和恢复线程

park系列方法关系到锁的实现,具体到锁在讨论。

定位于修改

Unsafe还可以用于定位对象的属性的内存位置,修改属性值。

  • 定位属性位置
    对于一个类来说,该类的一个对象,其某个属性值相对于该对象的内存空间的位置是固定的,而类Unsafe可以帮我们获取到这个偏移量。
  • 定位属性值
    有了属性的偏移量,Unsafe还提供了根据对象,偏移量来获取该对象的值的方法。

上述两个功能的方法参考如下:

public final class Unsafe {
    //获取类中属性的偏移量
    public native long objectFieldOffset(Field var1);

    //获取对象var1内部偏移量为var2的类型为Int的值
    public native int getIntVolatile(Object var1, long var2);

    //修改对象var1内部偏移量为var2的类型为Int的值为var4
    public native void putIntVolatile(Object var1, long var2, int var4);

分配与释放内存

Unsafe甚至提供手动操作内存的操作,正如类名一样,这些操作都是不安全的,我们不应该去使用。
贴一份参考资料,讲unsafe的Java中Unsafe类详解

到这里我么简单浏览了一下Unsafe类,也知道了关于CAS的一些操作, 那么AtomicInteger是如何使用Unsafe来实现同步操作的呢?

AtomicInteger

方法实现

正如开头提到的,AtomicInteger正如类名,完成原子性操作,其大部分操作都是使用Unsafe类来实现的。

public class AtomicInteger extends Number implements java.io.Serializable {
    //定义unsafe对象
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    //表示value值的偏移量
    private static final long valueOffset;
    //静态方法获取value的偏移量并赋给valueOffset
    static {
        try {
            valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile int value;

注意AtomicInteger内部的代码,回顾java对象jvm(二):对象加载浅谈
,除了对象头等知识外,还有一点再此做补充:

一个类一旦定义完毕,其内部字段相对于对象的偏移量就固定了。

于是我们就可以使用类而不是对象来获取属性的偏移量了。
valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));

然后就调用Unsafe的方法了,如下:

public class AtomicInteger extends Number implements java.io.Serializable {
    //...属性定义

    //典型的CAS操作,如果value==expect,则设置value=update
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    //设置value为newValue,并返回value
    public final int getAndSet(int newValue) {
        return unsafe.getAndSetInt(this, valueOffset, newValue);
    }

AtomicInteger剩下的方法都是诸如自增,自减等操作了。

同步机制

说到底其并未使用常见的synchronized或者锁机制,那AtomicInteger的同步是如何实现的呢?
秘诀就在于CAS。
部分源码如下:

public class AtomicInteger extends Number implements java.io.Serializable {
    //...
    //设置value为newValue,并返回value
    public final int getAndAdd(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta);
    }
    //...
}
public final class Unsafe {
    //...
    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;
    }
    //...
}

假设有3个线程,同时调用对于同一个AtomicInteger的getAndAdd(1)操作,然后仔细分析上述代码,就会发现,即使中间的过程可能会存在差异,但最后该AtomicInteger一定是3.

参考JAVA 中无锁的线程安全整数 AtomicInteger介绍和使用java中的CAS

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值