跳至主要內容

AtomicInteger

zheng大约 6 分钟面试java基础

1、介绍

AtomicInteger属于JUC并发包下的原子类,继承关系如下:

public class AtomicInteger extends Number implements java.io.Serializable

java 的并发机制中有三个特性:原子性、可见性和有序性。

synchronized可以保证可见性、有序性,无法保证原子性,AtomicInteger作用是保证原子性。

2、先看一个例子:

package cn.edu.bjut;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.*;

public class Main {
    private static volatile int a = 1;

    public static void main(String[] args) {
        // 创建线程工厂实例
        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("demo-pool-%d").build();
        // 创建线程池,核心线程数、最大线程数、空闲保持时间、队列长度、拒绝策略可自行定义
        ExecutorService pool = new ThreadPoolExecutor(5, 50, 0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
        // 
        for (int i = 0; i < 5; i++) {
            pool.submit(() -> {
                try {
                	System.out.println(a++);
                	Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
            });
        }
        System.out.println(a);
    }
}

结果:

1
4
4
3
5
2

定义变量a,保证a的可见性。用5个线程分别a++,但是结果不是5,每次都有不同的结果,且最后结果不是5,因为:

(1)每个线程从内存中读取a的值

(2)对a进行+1操作

(3)把a重新刷新回内存

当CPU切换分片 或者 第三步(3)线程未刷新回内存,此时我们线程就读相当于脏数据。

3、再看一个例子:

package cn.edu.bjut;


import com.google.common.util.concurrent.ThreadFactoryBuilder;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class Main {
    private static AtomicInteger a = new AtomicInteger();

    public static void main(String[] args) {
        // 创建线程工厂实例
        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("demo-pool-%d").build();
        // 创建线程池,核心线程数、最大线程数、空闲保持时间、队列长度、拒绝策略可自行定义
        ExecutorService pool = new ThreadPoolExecutor(5, 50, 0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
        // 
        for (int i = 0; i < 5; i++) {
            pool.submit(() -> {
                try {
                		System.out.println(a.incrementAndGet());
                		Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
            });
        }
		System.out.println(a);
    }
}

结果:

2
4
1
3
5
5

利用AtomicInteger定义变量a,保证a的原子性。用5个线程分别a++,但是结果不是5,每次都有不同的结果,但是最后结果是5,因为:

4、AtomicInteger如何保证原子性

(1)、源码分析

AtomicInteger使用了incrementAndGet函数(类中还有很多个API都是利用相同的方式保证原子性)

    private static final Unsafe U = Unsafe.getUnsafe();
    private static final long VALUE = U.objectFieldOffset(AtomicInteger.class, "value");

    private volatile int value;

	/**
     * 以原子方式递增当前值,
     *
     * 相当于addAndGet(1)
     *
     * @return 更新的值
     */
    public final int incrementAndGet() {
        return U.getAndAddInt(this, VALUE, 1) + 1;
    }

底层使用的是unsafe的getAndAddInt方法,对象 U 和 参数 VALUE

(1)U

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

利用的是compareAndSwapInt,又称CAS,即比较并替换,实现并发算法时常用到的一种技术。

CAS操作包含三个操作数:内存位置、预期原值及新值。

执行CAS操作的时候,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值,否则,处理器不做任何操作。

Unsafe类使Java语言拥有了类似C语言指针一样操作内存空间的能力,这无疑也增加了程序发生相关指针问题的风险。在程序中过度、不正确使用Unsafe类会使得程序出错的概率变大,使得Java这种安全的语言变得不再“安全”,因此对Unsafe的使用一定要慎重。在jdk1.9中,对Usafe进行了删除。

(2)VALUE

 private static final long VALUE = U.objectFieldOffset(AtomicInteger.class, "value");

VALUE是 long 类型的,代表的含义就是对象的地址的偏移量。

U.getAndAddInt(this, VALUE, 1) + 1;

U 通过 getAndAddInt() 方法,对原先对象的地址进行了加 1 操作,得到一个最新的值,然后+1;

那么怎么保证 getAndAddInt() 方法是最新的值呢?

@HotSpotIntrinsicCandidate
public final int getAndAddInt(Object o, long offset, int delta) {
    int v;
    do {
        v = getIntVolatile(o, offset);
    } while (!weakCompareAndSetInt(o, offset, v, v + delta));
    return v;
}

底层通过 weakCompareAndSetInt 这个CAS机制来完成的增加操作:

param1:o 是当前对象

param2:offset 表示内存地址的偏移量

param3:v + delta 表示要增加的值

AtomicInteger的原理就是这,主要是通过Usafe的方式来完成的。Usafe又是通过CAS机制来实现的。

CAS算法是乐观锁的一种,Java原子类中的递增操作就通过CAS自旋实现的。

(2)、源码注释

package java.util.concurrent.atomic;

import java.lang.invoke.VarHandle;
import java.util.function.IntBinaryOperator;
import java.util.function.IntUnaryOperator;
import jdk.internal.misc.Unsafe;

/**
 * 一个int值,可以进行原子更新
 */
public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    /*
     * 该类打算使用VarHandles实现,但存在未解析的循环启动依赖项。
     */
    private static final Unsafe U = Unsafe.getUnsafe();
    private static final long VALUE
        = U.objectFieldOffset(AtomicInteger.class, "value");

    private volatile int value;

    /**
     * 构造函数,使用给定的初始值创建新的AtomicInteger
     *
     * @param initialValue the initial value
     */
    public AtomicInteger(int initialValue) {
        value = initialValue;
    }

    /**
     * 构造函数,默认的AtomicInteger的value为0
     */
    public AtomicInteger() {
    }

    /**
     * @return 返回当前值
     * 
     */
    public final int get() {
        return value;
    }

    /**
     * 将值设置为newValue
     * 
     * @param newValue 指定的新值
     */
    public final void set(int newValue) {
        value = newValue;
    }

    /**
     * 将值设置为newValue
     * 
     * @param newValue 指定的新值
     * @since 1.6
     */
    public final void lazySet(int newValue) {
        U.putIntRelease(this, VALUE, newValue);
    }

    /**
     *
     *原子性地将值设置为newValue,并返回旧值
     *
     * @param newValue 指定的新值
     * @return 返回旧值
     */
    public final int getAndSet(int newValue) {
        return U.getAndSetInt(this, VALUE, newValue);
    }

    /**
     * CAS
     *
     * 如果当前值等于expectedValue,原子将该值设置为newValue
     *
     * @param expectedValue 指定的期望值
     * @param newValue 指定的新值
     * @return 如果成功返回true,实际值与预期值不相等返回false
     */
    public final boolean compareAndSet(int expectedValue, int newValue) {
        return U.compareAndSetInt(this, VALUE, expectedValue, newValue);
    }

    /**
     * 如果当前值等于expectedValue,原子将该值设置为newValue}
     *
     * @param expectedValue 指定的期望值
     * @param newValue 指定的新值
     * @return 如果成功返回true
     * @since 9
     */
    public final boolean weakCompareAndSetPlain(int expectedValue, int newValue) {
        return U.weakCompareAndSetIntPlain(this, VALUE, expectedValue, newValue);
    }

    /**
     * 以原子方式递增当前值,
     *
     * 相当于getAndAdd(1)
     *
     * @return 先前的值
     */
    public final int getAndIncrement() {
        return U.getAndAddInt(this, VALUE, 1);
    }

    /**
     * 原子递减当前值,
     *
     * 相当于getAndAdd(-1)
     *
     * @return 先前的值
     */
    public final int getAndDecrement() {
        return U.getAndAddInt(this, VALUE, -1);
    }

    /**
     * 以原子方式将给定值与当前值相加
     * 
     * @param delta 要加的值
     * @return 先前的值
     */
    public final int getAndAdd(int delta) {
        return U.getAndAddInt(this, VALUE, delta);
    }

    /**
     * 以原子方式递增当前值,
     *
     * 相当于addAndGet(1)
     *
     * @return 更新的值
     */
    public final int incrementAndGet() {
        return U.getAndAddInt(this, VALUE, 1) + 1;
    }

    /**
     * 以原子方式递减当前值

     * 相当于addAndGet(-1)
     *
     * @return 更新的值
     */
    public final int decrementAndGet() {
        return U.getAndAddInt(this, VALUE, -1) - 1;
    }

    /**
     * 以原子方式将给定值与当前值相加
     * 
     * @param delta 要添加的值
     * @return 更新的值
     */
    public final int addAndGet(int delta) {
        return U.getAndAddInt(this, VALUE, delta) + delta;
    }

    /**
     * 使用应用给定函数的结果以原子方式更新当前值,返回先前的值
     * 该函数应该没有副作用,因为当尝试的更新由于线程之间的争用而失败时,可以重新应用该函数
     *
     * @param updateFunction 无副作用的功能
     * @return 先前的值
     * @since 1.8
     */
    public final int getAndUpdate(IntUnaryOperator updateFunction) {
        int prev = get(), next = 0;
        for (boolean haveNext = false;;) {
            if (!haveNext)
                next = updateFunction.applyAsInt(prev);
            if (weakCompareAndSetVolatile(prev, next))
                return prev;
            haveNext = (prev == (prev = get()));
        }
    }

    /**
     * 使用应用给定函数的结果以原子方式更新当前值,返回更新后的值。
     * 该函数应该没有副作用,因为当尝试的更新由于线程之间的争用而失败时,可以重新应用该函数
     *
     * @param updateFunction 无副作用的功能
     * @return 更新后的值
     * @since 1.8
     */
    public final int updateAndGet(IntUnaryOperator updateFunction) {
        int prev = get(), next = 0;
        for (boolean haveNext = false;;) {
            if (!haveNext)
                next = updateFunction.applyAsInt(prev);
            if (weakCompareAndSetVolatile(prev, next))
                return next;
            haveNext = (prev == (prev = get()));
        }
    }
	.................
}
上次编辑于:
贡献者: 郑天祺