java cas是原子性的么_CAS机制与原子性

本文探讨了Java中的CAS(Compare and Swap)机制及其原子性。通过示例展示了如何使用`AtomicInteger`和`Unsafe`类实现线程安全的原子操作。CAS操作在高并发环境下能有效避免锁的开销,但也存在循环重试导致的CPU资源占用问题,以及ABA问题。Java并发包提供了一系列原子类,如AtomicInteger,以支持不同类型的原子更新,以及LongAdder等高性能计数器。
摘要由CSDN通过智能技术生成

CAS机制与原子性

学习材料来源于网络

如有侵权,联系删除

概念

Compare and swap比较和交换。属于硬件同步原语,处理器提供了基本内存操作的原子性保证。CAS操作需要输入两个数值;一个旧值A(期望操作前的值)和一个新值B,在操作期间先比较下旧值有没有发生变化,如果没有发生变化,才交换成新值,发生了变化则不交换。

JAVA中的sun.misc.Unsafe类,提供了compareAndSwapInt()和compareAndSwapLong()等几个方法实现CAS.

559d56e20f1d6ea8f61205c1d55d0528.png

示例1

import java.util.concurrent.atomic.AtomicInteger;

// 两个线程,对 i 变量进行递增操作

public class LockDemo {

// volatile int i = 0;

AtomicInteger i = new AtomicInteger(0);

public void add() {

// TODO xx00

// i++;// 三个步骤

i.incrementAndGet();

}

public static void main(String[] args) throws InterruptedException {

LockDemo ld = new LockDemo();

for (int i = 0; i < 2; i++) {

new Thread(() -> {

for (int j = 0; j < 10000; j++) {

ld.add();

}

}).start();

}

Thread.sleep(2000L);

System.out.println(ld.i);

}

}

使用CAS操作,保证原子性

package icu.shaoyayu.multithreading.chapter2;

import sun.misc.Unsafe;

import java.lang.reflect.Field;

/**

* @author shaoyayu

* @date 2020/11/29

* @E_Mail

* @Version 1.0.0

* @readme :

*/

public class LockDemo1 {

volatile int value = 0;

// 直接操作内存,修改对象,数组内存....强大的API

static Unsafe unsafe;

private static long valueOffset;

static {

try {

// 反射技术获取unsafe值

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

field.setAccessible(true);

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

// 获取到 value 属性偏移量(用于定于value属性在内存中的具体地址)

valueOffset = unsafe.objectFieldOffset(LockDemo1.class

.getDeclaredField("value"));

} catch (Exception ex) {

ex.printStackTrace();

}

}

public void add() {

// CAS + 循环 重试

int current;

do {

// 操作耗时的话, 那么 线程就会占用大量的CPU执行时间

current = unsafe.getIntVolatile(this, valueOffset);

} while (!unsafe.compareAndSwapInt(this, valueOffset, current, current + 1));

// 可能会失败

}

public static void main(String[] args) throws InterruptedException {

LockDemo1 ld = new LockDemo1();

for (int i = 0; i < 2; i++) {

new Thread(() -> {

for (int j = 0; j < 10000; j++) {

ld.add();

}

}).start();

}

Thread.sleep(2000L);

System.out.println(ld.value);

}

}

使用原子性操作的话可以保持对某个内存地址的原子性

但是这样一直处于循环状态中,会占用CPU资源,所有可以使用同步关键字,避免这种问题

J.U.C包内的原子操作封装类

AtomicBoolean:原子更新布尔类型

AtomicInteger:原子更新整型

AtomicLong:原子更新长整型

示例2

package icu.shaoyayu.multithreading.chapter2;

import java.util.concurrent.atomic.AtomicInteger;

/**

* @author shaoyayu

* @E_Mail

* @Version 1.0.0

* @readme :

*/

public class LockDemo3 {

//原子性操作Int提供的对象

AtomicInteger value = new AtomicInteger(0);

public void add() {

// TODO xx00

//

value.incrementAndGet();

}

public static void main(String[] args) throws InterruptedException {

LockDemo3 ld = new LockDemo3();

for (int i = 0; i < 2; i++) {

new Thread(() -> {

for (int j = 0; j < 10000; j++) {

ld.add();

}

}).start();

}

Thread.sleep(2000L);

System.out.println(ld.value);

}

}

源码分析:

/ **

*以原子方式将当前值增加1。

*

* @返回更新的值

* /

public final int incrementAndGet() {

return unsafe.getAndAddInt(this, valueOffset, 1) + 1;

}

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;

}

处理上面举例的数据类型外,还有

48933d18cbabbc04c56971599b8654c9.png

AtomicBoolean:原子更新布尔类型

AtomicInteger:原子更新整型

AtomicLong:原子更新长整型

AtomiclntegerArray:原子更新整型数组里的元素。

AtomicLongArray:原子更新长整型数组里的元素。

AtomicReferenceArray:原子更新引用类型数组里的元素。

AtomiclntegerFieldUpdater:原子更新整型的字段的更新器。

AtomicLongFieldUpdater:原子更新长整型字段的更新器。

AtomicReferenceFieldUpdater:原子更新引用类型里的字段。

AtomicReference:原子更新引用类型。

AtomicStampedReference:原子更新带有版本号的引用类型

AtomicMarkableReference:原子更新带有标记位的引用类型。

1.8更新

更新器:DoubleAccumulator、LongAccumulator

计数器:DoubleAdder、LongAdder

计数器增强版,高并发下性能更好

频繁更新但不太频繁读取的汇总统计信息时使用分成多个操作单元,不同线程更新不同的单元只有需要汇总的时候才计算所有单元的操作

计数器

为多个线程共享的变量创建多个内存地址供多个线程操作,最后一致性汇总给线程。

package icu.shaoyayu.multithreading.chapter2;

import java.util.concurrent.atomic.AtomicLong;

import java.util.concurrent.atomic.LongAdder;

/**

* @author shaoyayu

* @date 2020/11/29

* @E_Mail

* @Version 1.0.0

* @readme :

*/

// 测试用例: 同时运行2秒,检查谁的次数最多

public class LongAdderDemo {

private long count = 0;

// 同步代码块的方式

public void testSync() throws InterruptedException {

for (int i = 0; i < 3; i++) {

new Thread(() -> {

long starttime = System.currentTimeMillis();

while (System.currentTimeMillis() - starttime < 2000) { // 运行两秒

synchronized (this) {

++count;

}

}

long endtime = System.currentTimeMillis();

System.out.println("SyncThread spend:" + (endtime - starttime) + "ms" + " v" + count);

}).start();

}

}

// Atomic方式

private AtomicLong acount = new AtomicLong(0L);

public void testAtomic() throws InterruptedException {

for (int i = 0; i < 3; i++) {

new Thread(() -> {

long starttime = System.currentTimeMillis();

while (System.currentTimeMillis() - starttime < 2000) { // 运行两秒

acount.incrementAndGet(); // acount++;

}

long endtime = System.currentTimeMillis();

System.out.println("AtomicThread spend:" + (endtime - starttime) + "ms" + " v-" + acount.incrementAndGet());

}).start();

}

}

// LongAdder 方式

private LongAdder lacount = new LongAdder();

public void testLongAdder() throws InterruptedException {

for (int i = 0; i < 3; i++) {

new Thread(() -> {

long starttime = System.currentTimeMillis();

while (System.currentTimeMillis() - starttime < 2000) { // 运行两秒

lacount.increment();

}

long endtime = System.currentTimeMillis();

System.out.println("LongAdderThread spend:" + (endtime - starttime) + "ms" + " v-" + lacount.sum());

}).start();

}

}

public static void main(String[] args) throws InterruptedException {

LongAdderDemo demo = new LongAdderDemo();

demo.testSync();

demo.testAtomic();

demo.testLongAdder();

}

}

运行结果:

SyncThread spend:2000ms v22305402

SyncThread spend:2005ms v22305404

SyncThread spend:2005ms v22305404

AtomicThread spend:2000ms v-71491578

AtomicThread spend:2000ms v-71492707

LongAdderThread spend:2000ms v-116795972

LongAdderThread spend:2000ms v-116796436

AtomicThread spend:2000ms v-71951742

LongAdderThread spend:2000ms v-118008296

源码:

/**

* Adds the given value.

*

* @param x the value to add

*/

public void add(long x) {

Cell[] as; long b, v; int m; Cell a;

if ((as = cells) != null || !casBase(b = base, b + x)) {

boolean uncontended = true;

if (as == null || (m = as.length - 1) < 0 ||

(a = as[getProbe() & m]) == null ||

!(uncontended = a.cas(v = a.value, v + x)))

longAccumulate(x, null, uncontended);

}

}

/ **

*返回当前总和。返回的值为 NOT em>

*原子快照;在没有并发

*更新的情况下调用将返回准确的结果,但是

*在计算总和时发生的并发更新

*可能不会被合并。

*

* @返回总和

* /

public long sum() {

Cell[] as = cells; Cell a;

long sum = base;

if (as != null) {

for (int i = 0; i < as.length; ++i) {

if ((a = as[i]) != null)

sum += a.value;

}

}

return sum;

}

适合频繁更新,但不适合频繁读取

支持CAS的其他自定义方法的增强版

package icu.shaoyayu.multithreading.chapter2;

import java.util.concurrent.atomic.LongAccumulator;

import java.util.function.LongBinaryOperator;

/**

* @author shaoyayu

* @E_Mail

* @Version 1.0.0

* @readme :

*/

// LongAdder增强版,处理累加之外,可以自行定义其他计算

public class LongAccumulatorDemo {

public static void main(String[] args) throws InterruptedException {

LongAccumulator accumulator = new LongAccumulator(new LongBinaryOperator() {

@Override

public long applyAsLong(long left, long right) {

// 返回最大值,这就是自定义的计算

return left > right ? left : right;

}

}, 0);

// 1000个线程

for (int i = 0; i < 1000; i++) {

int finalI = i;

new Thread(() -> {

accumulator.accumulate(finalI); // 此处实际就是执行上面定义的操作

}).start();

}

Thread.sleep(2000L);

System.out.println(accumulator.longValue()); // 打印出结果

}

}

CAS的三个问题

1.循环+CAS,自旋的实现让所有线程都处于高频运行,争抢CPU执行时间的状态。如果操作长时间不成功,会带来很大的CPU资源消耗。

2.仅针对单个变量的操作,不能用于多个变量来实现原子操作。

3.ABA问题。(此处结合代码理解))。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值