原子计数器AtomicInteger
☁️前言
在梳理Redis过程中,Redis能实现的原子技术与JDK自带的原子计数的区别有什么?
☀️ 应用
10个线程对同一资源【count】做加操作,每个线程加10000次,预期count的值为100000。看一下如下不同的实现。
线程不安全示例
package test;
import java.util.concurrent.CountDownLatch;
/**
* 线程不安全方法
* @author Abner G
* @date 2022/2/22 21:30
*/
public class UnSafeAdd {
private static int threadCount=10;
/**
* 使用CountDownLatch 等待线程跑完
*/
private static CountDownLatch countDownLatch =new CountDownLatch(threadCount);
private static Integer count=0;
/**
* 实现runnable 实现多线程
*/
private static class AddClass implements Runnable{
@Override
public void run() {
/**
* 非线程安全的方法
*/
for (int i = 0; i < 10000; i++) {
count++;
}
/**
* 将count值减1
*/
countDownLatch.countDown();
}
}
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[threadCount];
for (int i = 0; i < threads.length; i++) {
/**
* 设置线程
*/
threads[i] = new Thread(new AddClass());
}
for (Thread thread : threads) {
/**
* 开启线程
*/
thread.start();
}
/**
* 调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
*/
countDownLatch.await();
System.out.println(count);
}
}
使用synchronized 悲观锁
线程不安全的情况我们知道要加锁,可能会加synchronized 悲观锁,实现如下,并且结果也达到了预期,但是synchronized 是线程阻塞的,属于重量级别的操作。
package test;
import java.util.concurrent.CountDownLatch;
/**
*
* @author Abner G
* @date 2022/2/22 21:47
*/
public class SynchronizedAdd {
private static Integer threadCount=10;
private static CountDownLatch countDownLatch= new CountDownLatch(threadCount);
private static Integer count=0;
/**
* 加悲观锁
*/
synchronized private static void addMethod(){
count++;
}
private static class AddClass implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
/**
* 调用同步方法
*/
addMethod();
}
countDownLatch.countDown();
}
}
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[threadCount];
for (int i = 0; i < threads.length; i++) {
threads[i]=new Thread(new AddClass());
}
for (Thread thread : threads) {
thread.start();
}
countDownLatch.await();
System.out.println(count);
}
}
AtomicInteger 实现
Java从JDK 1.5开始提供了java.util.concurrent.atomic包,乐观 ,用CAS实现。
实现如下,并且也达到预期。synchronized与AtomicInteger 区别
CAS是Compare And Set的一个简称
package test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author Abner G
* @date 2022/2/22 21:58
*/
public class AtomicAdd {
private static Integer threadCount=10;
private static CountDownLatch countDownLatch= new CountDownLatch(threadCount);
/**
* 原子操作类
*/
private static AtomicInteger count=new AtomicInteger(0);
private static class AddClass implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
/**
* 增加指定的数据后返回增加后的数据
*/
count.addAndGet(1);
}
countDownLatch.countDown();
}
}
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[threadCount];
for (int i = 0; i < threads.length; i++) {
threads[i]=new Thread(new AddClass());
}
for (Thread thread : threads) {
thread.start();
}
countDownLatch.await();
System.out.println(count.get());
}
}
⭐️源码分析
CAS是Compare And Set的一个简称
使用JDK 1.7版本,执行过程如下
AtomicInteger 类中主要执行
/**
* 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;
}
}
get方法 获得value是一个volatile变量,不同线程对这个变量进行操作时具有可见性,修改与写入操作都会存入主存中,并通知其他cpu中该变量缓存行无效,保证了每次读取都是最新的值
调用 unsafe 类中的compareAndSwapInt方法
compareAndSwapInt方法使用native关键字修饰,说明调用了本地方法区中的接口。
查看OpenJDK
unsafe.cpp 源码地址
调用 Atomic::cmpxchg 方法,这个类的实现是跟操作系统有关, 跟CPU架构也有关.
结论:CAS的原子性实际上是CPU实现的
操作过程
- (A)已知当前内存里面的值current和预期要修改成的值new传入
- (B)内存中AtomicInteger对象地址对应的“旧”真实值(因为有可能别修改)real与current对比,
- (C)相等表示real未被修改过,是“安全”的,将new赋给real结束然后返回;不相等说明real已经被修改,结束并重新执行(A)直到修改成功
JDK 11
/**
* Atomically adds the given value to the current value of a field
* or array element within the given object {@code o}
* at the given {@code offset}.
*
* @param o object/array to update the field/element in
* @param offset field/element offset
* @param delta the value to add
* @return the previous value
* @since 1.8
*/
@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;
}
————————————————
版权声明:本文为CSDN博主「littlehaes」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/littlehaes/article/details/105156443
weakCompareAndSetInt方法底层
🏆 总结
CAS的原子性实际上是CPU实现的