基于版本jdk1.7.0_80

java.util.concurrent.atomic.AtomicInteger

代码如下

/*
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/ /*
*
*
*
*
*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
* http://creativecommons.org/publicdomain/zero/1.0/
*/ package java.util.concurrent.atomic;
import sun.misc.Unsafe; /**
* An {@code int} value that may be updated atomically. See the
* {@link java.util.concurrent.atomic} package specification for
* description of the properties of atomic variables. An
* {@code AtomicInteger} is used in applications such as atomically
* incremented counters, and cannot be used as a replacement for an
* {@link java.lang.Integer}. However, this class does extend
* {@code Number} to allow uniform access by tools and utilities that
* deal with numerically-based classes.
*
* @since 1.5
* @author Doug Lea
*/
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L; // 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; /**
* Creates a new AtomicInteger with the given initial value.
*
* @param initialValue the initial value
*/
public AtomicInteger(int initialValue) {
value = initialValue;
} /**
* Creates a new AtomicInteger with initial value {@code 0}.
*/
public AtomicInteger() {
} /**
* Gets the current value.
*
* @return the current value
*/
public final int get() {
return value;
} /**
* Sets to the given value.
*
* @param newValue the new value
*/
public final void set(int newValue) {
value = newValue;
} /**
* Eventually sets to the given value.
*
* @param newValue the new value
* @since 1.6
*/
public final void lazySet(int newValue) {
unsafe.putOrderedInt(this, valueOffset, newValue);
} /**
* Atomically sets to the given value and returns the old value.
*
* @param newValue the new value
* @return the previous value
*/
public final int getAndSet(int newValue) {
for (;;) {
int current = get();
if (compareAndSet(current, newValue))
return current;
}
} /**
* 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 true if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
} /**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
* and does not provide ordering guarantees, so is only rarely an
* appropriate alternative to {@code compareAndSet}.
*
* @param expect the expected value
* @param update the new value
* @return true if successful.
*/
public final boolean weakCompareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
} /**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
} /**
* Atomically decrements by one the current value.
*
* @return the previous value
*/
public final int getAndDecrement() {
for (;;) {
int current = get();
int next = current - 1;
if (compareAndSet(current, next))
return current;
}
} /**
* Atomically adds the given value to the current value.
*
* @param delta the value to add
* @return the previous value
*/
public final int getAndAdd(int delta) {
for (;;) {
int current = get();
int next = current + delta;
if (compareAndSet(current, next))
return current;
}
} /**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
} /**
* Atomically decrements by one the current value.
*
* @return the updated value
*/
public final int decrementAndGet() {
for (;;) {
int current = get();
int next = current - 1;
if (compareAndSet(current, next))
return next;
}
} /**
* Atomically adds the given value to the current value.
*
* @param delta the value to add
* @return the updated value
*/
public final int addAndGet(int delta) {
for (;;) {
int current = get();
int next = current + delta;
if (compareAndSet(current, next))
return next;
}
} /**
* Returns the String representation of the current value.
* @return the String representation of the current value.
*/
public String toString() {
return Integer.toString(get());
} public int intValue() {
return get();
} public long longValue() {
return (long)get();
} public float floatValue() {
return (float)get();
} public double doubleValue() {
return (double)get();
} }

1. CAS

CAS Compare And Swap,顾名思义,比较并交换

一般的形式是cas(pointer, expect, update),其含义为:比较pointer指向的值与expect,如果两者相等,则将pointer指向的值更新为update并返回ture,如果两者不等,返回false

如果有多个线程同时对同一个pointer调用cas函数,那么只有一个线程能成功pointer指向的值并返回true,其他线程都会返回false

也就是说这是一个原子操作,它需要cpu层面的支持,比方说cmpxchg函数

JDK内部实现了CAS操作,但是不对外开放(用反射强行拿当然可以)

2. sun.misc.Unsafe

这里我们主要用的是Unsafe提供的CAS功能,提供了如下的代码

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

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

    public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

native的,估计底层的jni代码会根据运行平台调用对应的汇编码,比方说如果是intel 的x86架构,就cmpxchg走一走

3. 实现原理

AtomicInteger内部维护了一个volatile类型的int变量value

value用volatile修饰是为了利用volatile带来的内存可见性语义。

想象一个场景,有两个线程A和B,线程B先修改了value,线程A再去读value。

如果没有volatile修饰,可能B读到的是线程A修改之前的值。但是加上volatile之后,线程A对value的修改一定对B可见,所以B可以读到最新的值。(实现原理是线程A在写入value之后,插入一条storeload内存屏障,对于Intel的x86架构,就是插入一条带lock前缀的空转指令(lock addl $0x0,(%esp)),这个操作会导致线程A所在的core的cache line(其中肯定包含了value)回写到主存,以及使其他core中对应的cache line(其中肯定包含了value)被设为invalid,那么线程B所在的其他core想要读取value的时候,由于cache line被invalid,就只能从主存中取值,这时拿到的值就是线程A更新后最新的值了)

但是volatile的内存可见性语义还不够,同样设想一个场景,两个线程A和B在不同的core上工作,并发对value=0进行自增操作。两个core并发的将value加载到cache line,然后同时将value加载到寄存器中。线程A先对寄存器中的value自增(缓存一致性协议管不到寄存器),得到value=1,然后将其写入cache line,缓存一致性协议工作,将线程B所在的core的对应的cache line设置为invalid。这时线程B再执行,也将寄存器中的value自增,得到value=1,想要写入到cache line,但是对应的cache line已经是invalid了,于是从主存中取最新的value=1加载到cache line中来,然后写入,触发缓存一致性协议,将value=1写回到主存中。最终还是得到value=1的结果。

为了避免这种情况,最简单的想法就是加锁,将value作为临界区,多个线程不能同时操作,但是加锁是一个比较重的操作,比较浪费。

AtomicInteger的做法是volatile配合CAS使用,如果有线程想更新value,那么记下value的当前值expect,以及需要更新到的新值update=expect+1

然后调用cas指令:cas(value, expect, update)

在无竞争的情况下,cas直接更新成功

在有竞争的情况下,可能在执行cas之前,value被其他线程修改成了新值,那么由于cas条件中的expect与value的当前值不等,会导致cas操作失败并返回false,我们检测到cas操作失败,直接重试即可,总有一次能成功的。

4. 实现代码

    public final int incrementAndGet() {
for (;;) {//无限循环
int current = get();//获取value当前值
int next = current + 1;//算出value的期望值
if (compareAndSet(current, next))//尝试cas更新value
return next;//成功,函数返回
}//失败,继续cas,总有一次能更新成功的
}

挑increaseAndGet这一个方法就行了,很有代表性

    public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

compareAndSet的实现则是直接调用Unsafe的代码了,native的不好跟

java.util.concurrent.atomic包里还有其他的一堆class,AtomicLong,AtomReference等,原理都差不多,在此就不赘述了

参考文献

聊聊并发(一)深入分析Volatile的实现原理

J.U.C并发框架源码阅读(一)AtomicInteger的更多相关文章

  1. J.U.C并发框架源码阅读(二)AbstractQueuedSynchronizer

    基于版本jdk1.7.0_80 java.util.concurrent.locks.AbstractQueuedSynchronizer 代码如下 /* * ORACLE PROPRIETARY/C ...

  2. J.U.C并发框架源码阅读(六)ConditionObject

    基于版本jdk1.7.0_80 java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject 代码如下 /** * Con ...

  3. J.U.C并发框架源码阅读(五)Semaphore

    基于版本jdk1.7.0_80 java.util.concurrent.Semaphore 代码如下 /* * ORACLE PROPRIETARY/CONFIDENTIAL. Use is sub ...

  4. J.U.C并发框架源码阅读(八)ArrayBlockingQueue

    基于版本jdk1.7.0_80 java.util.concurrent.ArrayBlockingQueue 代码如下 /* * ORACLE PROPRIETARY/CONFIDENTIAL. U ...

  5. J.U.C并发框架源码阅读(十三)ThreadPoolExecutor

    基于版本jdk1.7.0_80 java.util.concurrent.ThreadPoolExecutor 代码如下 /* * ORACLE PROPRIETARY/CONFIDENTIAL. U ...

  6. J.U.C并发框架源码阅读(十五)CopyOnWriteArrayList

    基于版本jdk1.7.0_80 java.util.concurrent.CopyOnWriteArrayList 代码如下 /* * Copyright (c) 2003, 2011, Oracle ...

  7. J.U.C并发框架源码阅读(三)ReentrantLock

    基于版本jdk1.7.0_80 java.util.concurrent.locks.ReentrantLock 代码如下 /* * ORACLE PROPRIETARY/CONFIDENTIAL. ...

  8. J.U.C并发框架源码阅读(十二)ConcurrentHashMap

    基于版本jdk1.7.0_80 java.util.concurrent.ConcurrentHashMap 代码如下 /* * ORACLE PROPRIETARY/CONFIDENTIAL. Us ...

  9. J.U.C并发框架源码阅读(七)CyclicBarrier

    基于版本jdk1.7.0_80 java.util.concurrent.CyclicBarrier 代码如下 /* * ORACLE PROPRIETARY/CONFIDENTIAL. Use is ...

随机推荐

  1. laravel5.5事件广播系统

    目录 1. 定义广播事件 1.1 广播名称 1.2 广播数据 1.3 广播队列 1.4 广播条件 2. 频道授权 2.1 定义授权路由 2.2 定义授权回调 3. 对事件进行广播 3.1 可以使用ev ...

  2. elasticsearch索引和映射

    目录 1. elasticsearch如何实现搜索 1.1 搜索实例 1.2 es中数据的类型 1.3 倒排索引 1.4 分析与分析器 1.4.1 什么是分析器 1.4.2 内置分析器种类 1.4.3 ...

  3. Spark 的情感分析

    Spark 的情感分析 本文描述了基于 Spark 如何构建一个文本情感分析系统.文章首先介绍文本情感分析基本概念和应用场景,其次描述采用 Spark 作为分析的基础技术平台的原因和本文使用到技术组件 ...

  4. 打开Vim/Vi代码高亮

    由于新装Vim/Vi 默认是没有打开代码高亮配置的,就看到有朋友一次次到网上去找各种配置.其实Vim默认带来配置文件的样本的,只需拷贝过来就可使用. 在用户根目录(~)中新建vim的配置文件 .vim ...

  5. CSS系列(7)CSS类选择器Class详解

    这一篇文章,以笔记形式写. 1,  CSS 类选择器详解 http://www.w3school.com.cn/css/css_selector_class.asp 知识点: (1)    使用类选择 ...

  6. mysql错误:Column ‘id’ in field list is ambiguous的解决方法

    [Err] 1052 - Column 'modify_time' in where clause is ambiguous 出错的语句: SELECT AVG(T.se)%60FROM( SELEC ...

  7. scheduled定时任务cron表达式知识地址

    https://www.cnblogs.com/javahr/p/8318728.html https://www.cnblogs.com/sunjie9606/archive/2012/03/15/ ...

  8. mongodb 部署

    安装mongodb-3.4 1)将安装包上传至服务器 2)对压缩文件进行解压 tar -zxvf mongodb-linux-x86_64-suse12-v3.4-latest.tar.gz 3)把解 ...

  9. tomcat调优(Mark)

    http://blog.csdn.net/jinwanmeng/article/details/7756591

  10. [洛谷P4149][IOI2011]Race

    题目大意:给一棵树,每条边有边权.求一条简单路径,权值和等于$K$,且边的数量最小. 题解:点分治,考虑到这是最小值,不满足可减性,于是点分中的更新答案的地方计算重复的部分要做更改,就用一个数组记录前 ...