//创建一个原子类的对象,并进行++ 操作,从0开始
AtomicInteger atomicInteger = new AtomicInteger();
atomicInteger.incrementAndGet();
复制代码
수행 내부 운영 봐
내부 구조 AtomicInteger
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
// CAS的核心类,由于java方法无法直接访问底层系统,需要通过本地(native)方法来访问
//Unsafe相当于一个后门,基于该类可以直接操作特定内存的数据
private static final Unsafe unsafe = Unsafe.getUnsafe();
//表示该变量值在内存中的偏移地址,因为Unsafe就是根据内存偏移地址获取数据的
//偏移量的理解:内存中存数数据的方式:一个存储数据的 实际地址=段首地址+偏移量
//对应的现实中 家庭地址= 小区地址+门牌号
private static final long valueOffset;
//使用volatile保证了多线程之间的内存可见性
private volatile int value;
// 创建对象的时候 就会将valueOffset的值获取到
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
/**
* 有参构造
*/
public AtomicInteger(int initialValue) {
value = initialValue;
}
/**
* 无参构造
*/
public AtomicInteger() {
}
复制代码
어떻게 각각의 증가가 자성이 있는지 확인하기
/**
*
*/
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
/**
*
* @param var1 : AtomicInteger对象
* @param var2 : valueOffset
* @param var4 : 固定值 1
* @Description :将value进行自增,并且返回自增的值
* @Author Licy
* @Date 2019/6/14 20:03
* @return int
*
*/
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
//可以看成compareAndSwapInt(obj, offset, expect, update)
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
复制代码
특정 프로세스
실제 값은 값에 저장된
클래스가로드 될 때, 또한 안전하지 않은 예를 인수
valueOffset을 정의하고, 상기 취득 된 오프셋 값이 될 때 실행 코드, 정적 코드 블록의 정적 블록의 변화 값을 초기화
getAndAddInt 함수 수득 VAR5 valueOffset 나타내는 특정 값, 즉 값 값인
compareAndSwapInt 기능을 의미 :이 단계가 성공하지 못한 경우 (OBJ)의 값과 너무 오래 그가 업데이 트를 업데이트로, 다른 스레드가이 변수를 변경 없다는 것을 증명 동일한 기대한다면, CAS는, 스핀 모드 동작의 다음 사용은 CAS를 계속
두 단계와의 비교는 CPU의 JNI 지시 완성 문제 원자되도록하는 수단으로, 사실, 언뜻 배치
기본이되는 원칙
CAS 기본 사용 JNI는 핫스팟 소스 코드가있는 경우, 당신이에 Unsafe.cpp 구현 찾을 수 달성하기 위해 C 코드를 호출합니다 :
발생하는 문제
1, ABA 문제
CAS 검사를 사용하는 경우 CAS가 업데이트되어 변경되지 않은 경우 변경되지 않은 운영 값을 낮은 값을 확인하는 시간이 필요하지만, 값이 인 경우, A B되었다, 그는 A는, 당신은 그것을 찾을 수 있습니다 값은 변경할 수 있지만, 실제로는 변경되지 않습니다. 이것은 ABA 문제 CAS입니다
일반적인 솔루션은 버전 번호입니다. 버전 번호의 버전 번호를 더한 다음, 그 ABA 1A-2B-3A 될 때마다 변수가 업데이트 될 때, 변수의 앞에 추가된다.
현재 그것은 ABA 문제를 해결하기 위해 원자 패키지 JDK의 클래스 AtomicStampedReference를 제공합니다. 이 클래스의 작업에있어서의 compareAndSet 현재 참조 예상 기준 같으며 모든 동일한 원자 기준 값 및 상기 플래그가 지정된 갱신 값으로 설정된 경우 현재 마크가 예상 플래그와 같은지 검사한다.
2, 사이클 시간은 큰 지출
CAS가 실패하면 긴 스핀이 매우 큰 CPU의 실행 비용을 가져올 경우, 장소에 회전합니다.
그러나이 문제는 jdk8에서 해결되었습니다
자바 (8)는 CAS의 메커니즘을 최적화하기
java.util.concurrency.atomic.LongAdder
java8 새로운 클래스, 메소드 원자 통합 값
일반 과정
public class LongAdder extends Striped64 implements Serializable {
//.....
}
abstract class Striped64 extends Number {
/**
*
*/
transient volatile Cell[] cells;
/**
* Base value, used mainly when there is no contention, but also as
* a fallback during table initialization races. Updated via CAS.
*/
transient volatile long base;
//
@sun.misc.Contended static final class Cell {
volatile long value;
Cell(long x) { value = x; }
final boolean cas(long cmp, long val) {
return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
}
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE;
private static final long valueOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> ak = Cell.class;
valueOffset = UNSAFE.objectFieldOffset
(ak.getDeclaredField("value"));
} catch (Exception e) {
throw new Error(e);
}
}
}
}
复制代码
LongAdder 클래스는 Striped64을 상속
Striped64 클래스는 데이터 지연로드 셀 [] 및 추가베이스 인스턴스 필드를 유지
데이터 크기는 N (2)의 전력은 내부 액세스 스레드의 해시 값을 이용하여 각각의 스레드
사용 통계 경합 변형 의사 셀 데이터 공유의 발생을 방지하기 위해, 다른 운영 체제 자리 캐시 라인 샘플 크기를 해결하기 위해, 거짓 공유 (거짓 공유 월호)의 경우를 해결하는 것으로
public class LongAdder extends Striped64 implements Serializable {
private static final long serialVersionUID = 7249069246863182397L;
public LongAdder() {
}
public void add(long x) {
// as是Striped64中的cells属性
// b是Striped64中的base属性
// v是当前线程hash到的Cell中存储的值
// m是cells的长度减1,hash时作为掩码使用
// a是当前线程hash到的Cell
Cell[] as; long b, v; int m; Cell a;
// 条件1:cells不为空,说明出现过竞争,cells已经创建
// 条件2:cas操作base失败,说明其它线程先一步修改了base,正在出现竞争
if ((as = cells) != null || !casBase(b = base, b + x)) {
// true表示当前竞争还不激烈
// false表示竞争激烈,多个线程hash到同一个Cell,可能要扩容
boolean uncontended = true;
// 条件1:cells为空,说明正在出现竞争,上面是从条件2过来的
// 条件2:应该不会出现
// 条件3:当前线程所在的Cell为空,说明当前线程还没有更新过Cell,应初始化一个Cell
// 条件4:更新当前线程所在的Cell失败,说明现在竞争很激烈,多个线程hash到了同一个Cell,应扩容
if (as == null || (m = as.length - 1) < 0 ||
// getProbe()方法返回的是线程中的threadLocalRandomProbe字段
// 它是通过随机数生成的一个值,对于一个确定的线程这个值是固定的
// 除非刻意修改它
(a = as[getProbe() & m]) == null ||
!(uncontended = a.cas(v = a.value, v + x)))
// 调用Striped64中的方法处理
longAccumulate(x, null, uncontended);
}
}
/**
* Equivalent to {@code add(1)}.
*/
public void increment() {
add(1L);
}
/**
* Equivalent to {@code add(-1)}.
*/
public void decrement() {
add(-1L);
}
复制代码
final void longAccumulate(long x, LongBinaryOperator fn,
boolean wasUncontended) {
// 存储线程的probe值
int h;
// 如果getProbe()方法返回0,说明随机数未初始化
if ((h = getProbe()) == 0) {
// 强制初始化
ThreadLocalRandom.current(); // force initialization
// 重新获取probe值
h = getProbe();
// 都未初始化,肯定还不存在竞争激烈
wasUncontended = true;
}
// 是否发生碰撞
boolean collide = false; // True if last slot nonempty
for (;;) {
Cell[] as; Cell a; int n; long v;
// cells已经初始化过
if ((as = cells) != null && (n = as.length) > 0) {
// 当前线程所在的Cell未初始化
if ((a = as[(n - 1) & h]) == null) {
// 当前无其它线程在创建或扩容cells,也没有线程在创建Cell
if (cellsBusy == 0) { // Try to attach new Cell
// 新建一个Cell,值为当前需要增加的值
Cell r = new Cell(x); // Optimistically create
// 再次检测cellsBusy,并尝试更新它为1
// 相当于当前线程加锁
if (cellsBusy == 0 && casCellsBusy()) {
// 是否创建成功
boolean created = false;
try { // Recheck under lock
Cell[] rs; int m, j;
// 重新获取cells,并找到当前线程hash到cells数组中的位置
// 这里一定要重新获取cells,因为as并不在锁定范围内
// 有可能已经扩容了,这里要重新获取
if ((rs = cells) != null &&
(m = rs.length) > 0 &&
rs[j = (m - 1) & h] == null) {
// 把上面新建的Cell放在cells的j位置处
rs[j] = r;
// 创建成功
created = true;
}
} finally {
// 相当于释放锁
cellsBusy = 0;
}
// 创建成功了就返回
// 值已经放在新建的Cell里面了
if (created)
break;
continue; // Slot is now non-empty
}
}
// 标记当前未出现冲突
collide = false;
}
// 当前线程所在的Cell不为空,且更新失败了
// 这里简单地设为true,相当于简单地自旋一次
// 通过下面的语句修改线程的probe再重新尝试
elseif (!wasUncontended) // CAS already known to fail
wasUncontended = true; // Continue after rehash
// 再次尝试CAS更新当前线程所在Cell的值,如果成功了就返回
elseif (a.cas(v = a.value, ((fn == null) ? v + x :
fn.applyAsLong(v, x))))
break;
// 如果cells数组的长度达到了CPU核心数,或者cells扩容了
// 设置collide为false并通过下面的语句修改线程的probe再重新尝试
elseif (n >= NCPU || cells != as)
collide = false; // At max size or stale
// 上上个elseif都更新失败了,且上个条件不成立,说明出现冲突了
elseif (!collide)
collide = true;
// 明确出现冲突了,尝试占有锁,并扩容
elseif (cellsBusy == 0 && casCellsBusy()) {
try {
// 检查是否有其它线程已经扩容过了
if (cells == as) { // Expand table unless stale
// 新数组为原数组的两倍
Cell[] rs = new Cell[n << 1];
// 把旧数组元素拷贝到新数组中
for (int i = 0; i < n; ++i)
rs[i] = as[i];
// 重新赋值cells为新数组
cells = rs;
}
} finally {
// 释放锁
cellsBusy = 0;
}
// 已解决冲突
collide = false;
// 使用扩容后的新数组重新尝试
continue; // Retry with expanded table
}
// 更新失败或者达到了CPU核心数,重新生成probe,并重试
h = advanceProbe(h);
}
// 未初始化过cells数组,尝试占有锁并初始化cells数组
elseif (cellsBusy == 0 && cells == as && casCellsBusy()) {
// 是否初始化成功
boolean init = false;
try { // Initialize table
// 检测是否有其它线程初始化过
if (cells == as) {
// 新建一个大小为2的Cell数组
Cell[] rs = new Cell[2];
// 找到当前线程hash到数组中的位置并创建其对应的Cell
rs[h & 1] = new Cell(x);
// 赋值给cells数组
cells = rs;
// 初始化成功
init = true;
}
} finally {
// 释放锁
cellsBusy = 0;
}
// 初始化成功直接返回
// 因为增加的值已经同时创建到Cell中了
if (init)
break;
}
// 如果有其它线程在初始化cells数组中,就尝试更新base
// 如果成功了就返回
elseif (casBase(v = base, ((fn == null) ? v + x :
fn.applyAsLong(v, x))))
break; // Fall back on using base
}
}
复制代码
먼저베이스의 값은 멀티 스레딩을 시작하여 누적 값을 유지하는, 거기의베이스에 축적되어
예를 들어, 불과 5되었다 축적하기 시작하고 동시 업데이트, 우리는 하위 CAS 메커니즘의 구현을 시작합니다 스레드의 과도한 번호를 발견
CSA 분할기구는 셀 어레이 내에 각각 배열 축적 CAS 용 셀의 내부 값을 다른 값으로 데이터의 세그먼트,이 방법은 각각의 스레드 다수이며, CAS 계산 압력 씌워 분산액 동일한 값으로 갱신 할 때 동시에 발생하는 셀의 값, 보상 스레딩 무한 루프의 상이한 세그먼트
이 클래스는 또한 구현 자동 분할 마이그레이션 내부 메커니즘, 즉 셀의 값은 CAS가 자동으로 다른 세포 CAS 연산 될 값 세그먼트 값을 찾을 것이다 실패 실행되는 경우
당신은 현재 누적 총을 얻을 LongAdder 원하고, 당신에게 다시 값을 추가 모든 세그먼트의 셀에 대한 기본 값이됩니다.