Atomic原子类详解

你的位置:内江吭惫挥金融集团 > 软装装饰 > Atomic原子类详解
Atomic原子类详解
发布日期:2024-06-30 15:09    点击次数:114

Atomic原子类详解

参考:

[1] http://www.toyiye.com/post/13169.html

[2] http://www.toyiye.com/post/13015.html

[3] http://www.toyiye.com/post/12945.html

[4] http://www.toyiye.com/post/12841.html

[5] http://www.toyiye.com/post/12748.html

为什么需要Atomic原子操作类?

在并发环境中,代码要是操作接洽的数据,就会产生资源竞争,导致结果远小于预期值

举例在A线程B线程中同期得到到变量数据为1,同期奉行变量+1操作,结果可能亦然1,存在脏读幻读问题,因为在兼并个程度中,资源是分享的,因此需要进行原子操作

volatile要津字

作用: 不容CPU缓存,径直从主内存得到数据,更新数据时讲述到其他线程,保证线程脱手时候数据的可见性,小心领导重排

在并发环境中,要是多个线程进行资源竞争,依旧无法措置脏读幻读问题,有序问题

CAS旨趣

CAS (compareAndSwap),汉文叫相比交换,是一种无锁原子算法,映射到操作系统便是一条CPU的原子领导,其作用是让CPU先进行相比两个值是否绝顶,然后原子地更新某个位置的值,其罢了形态是基于硬件平台的汇编领导,在intel的CPU中,使用的是cmpxchg领导,便是说CAS是靠硬件罢了的,从而在硬件层面升迁后果。

奉行经过是这么:它包含 3 个参数 CAS(V,E,N),V暗示要更新变量的值,E暗示预期值,N暗示新值。仅当 V值等于E值时,才会将V的值设为N,要是V值和E值不同,则讲明还是有其他线程完成更新,则现时列程则什么齐不作念,终末CAS 复返现时V的着实值。

当多个线程同期使用CAS 操作一个变量时,最多唯有一个会胜出,并得手更新,其余均会失败。失败的线程不会挂起,仅是被见告失败,况兼允许再次尝试(自旋),天然也允许罢了的线程毁灭操作。基于这么的旨趣,CAS 操作即使莫得锁,也不错幸免其他线程对现时列程的纷扰。

AtomicObject罢了

Unsafe是CAS的中枢类,Java无法径直拜访底层操作系统,而是通过腹地(native)活动来拜访。不外尽管如斯,JVM如故开了一个后门:Unsafe,它提供了硬件级别的原子操作。

package org.example;

import sun.misc.Unsafe;

import java.lang.reflect.Field;

import java.util.function.UnaryOperator;

public class AtomicObject<V> implements java.io.Serializable {

private static final long serialVersionUID = 4654671469794556979L;

private static final Unsafe unsafe;

private static final long valueOffset;

static {

try {

//得到属性

Field field = Unsafe.class.getDeclaredField("theUnsafe");

field.setAccessible(true);

unsafe = (Unsafe) field.get(null);

valueOffset = unsafe.objectFieldOffset

(AtomicObject.class.getDeclaredField("value"));

} catch (Exception ex) {

throw new Error(ex);

}

}

private volatile V value;

public AtomicObject(V v){

this.value = v;

}

public final V get() {

return value;

}

/**

* 相比现时对象的援用值是否等于预期值,要是绝顶,则将对象的援用值配置为新值,并复返true,

* 不然不作念任何操作,并复返 false

* 将对象进行偏移量,和预期值进行相比

* @param expect 预期值

* @param update 更新值

* @return boolean

*/

public final boolean compareAndSet(V expect, V update) {

return unsafe.compareAndSwapObject(this, valueOffset, expect, update);

}

/**

* 要是现时值不是预期值,则自旋,直到现时值是预期值,并复返现时值

* @param updateFunction 更新的活动

* @return v 现时值

*/

public final V getAndUpdate(UnaryOperator<V> updateFunction) {

V prev, next;

do {

prev = get();

next = updateFunction.apply(prev);

} while (!compareAndSet(prev, next));

return prev;

}

public final V getAndUpdate(V next) {

V prev;

do {

prev = get();

} while (!compareAndSet(prev, next));

return prev;

}

public long getValueOffset(){

return valueOffset;

}

}

其中,expect参数是预期的对象援用值,update参数是要更新的新对象援用值。

compareAndSwapObject活动的罢了使用了CPU的原子操作领导,不错保证在多线程环境下的线程安全性。它每每用于罢了一些需要线程安全的对象援用更新操作,举例单例模式的罢了、缓存的更新等。

需要隆重的是,compareAndSwapObject活动只可保证对象援用的线程安全性,要是对象自己不是线程安全的,则需要使用其他的同步机制来保证对象的线程安全性

代码体现:

领先检查现时对象是否握有预期值,要是握有则,更新x为新值

节略的说,CAS 需要你迥殊给出一个期许值,也便是你觉得这个变量咫尺应该是什么形态的。要是变量不是你思象的那样,讲明它还是被别东谈主修悔改了。你就需要从头读取,再次尝试修改就好了。

// Unsafe.h

virtual jboolean compareAndSwapObject(::java::lang::Object *, jlong, ::java::lang::Object *, ::java::lang::Object *);

// natUnsafe.cc

static inline bool

compareAndSwap (volatile jobject *addr, jobject old, jobject new_val)

{

jboolean result = false;

spinlock lock;

// 要是字段的地址与期许的地址绝顶则将字段的地址更新

if ((result = (*addr == old)))

*addr = new_val;

return result;

}

// natUnsafe.cc

jboolean sun::misc::Unsafe::compareAndSwapObject (jobject obj, jlong offset,

jobject expect, jobject update)

{

// 得到字段地址并休养为字符串

jobject *addr = (jobject*)((char *) obj + offset);

// 调用 compareAndSwap 活动进行相比

return compareAndSwap (addr, expect, update);

CAS症结

CAS诚然高效地措置了原子操作,然而如故存在一些劣势的,主要表咫尺三个方面:

1.自旋时刻太长

2.只可保证一个分享变量原子操作

3.ABA问题,不行保证线程的法例问题

线程prevfinalUnsafereturn发布于:江西省声明:该文不雅点仅代表作家本东谈主,搜狐号系信息发布平台,搜狐仅提供信息存储空间工作。