Java中的原子类Atomic底层的实现原理是CAS,那么CAS到底是怎么实现无锁的呢???本篇文章我就带领大家来自己动手实现CAS。虽然看我的讲解能理解基本原理,但是要想真的理解还得大家亲自去动手实践。
首先我们先从Java层面来探究CAS,下面是我们对AtomicInteger的测试代码 ,执行完毕后会发现原子类实例ai最终是精确的10000,而普通变量bi的值是一个小于10000的不固定的值。
package com.hw.review2022.concurrent;
import org.junit.Test;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class TestCAS {
/**
* 被多个线程访问的变量
*/
static AtomicInteger ai = new AtomicInteger(0);
static int bi = 0;
/**
* 对ai和bi执行1000次自增
*/
class Task implements Runnable {
//任务id
private int id;
public Task(int i) {
this.id = i;
}
@Override
public void run() {
for(int i = 0; i < 1000; i++) {
try{
if(i % 100 == 0) {
System.out.println("Thread name : " + Thread.currentThread().getName() + ", Task id : " + id + ", loop : " + i);
}
Thread.sleep(2);
ai.addAndGet(1);
bi++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
@Test
public void test() throws Exception {
/**
* 创建含有3个线程的线程池
*/
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(3);
/**
* 在线程池中执行10个任务
*/
for(int i = 0; i < 10; i++) {
executor.execute(new Task(i));
}
/**
* 等待线程池中的任务执行完毕后才去打印ai和bi的值
*/
executor.shutdown();
executor.awaitTermination(60, TimeUnit.SECONDS);
System.out.println("ai is right : " + ai);
System.out.println("bi is wrong : " + bi);
}
}
核心代码分析
AtomicInteger的addAndGet代码如下
private volatile int value;
private static final long valueOffset;
/**
* 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) {
return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
}
this代表了当前这个AtomicInteger实例对象ai(后面都用ai表示)
valueOffset:
首先我们看一下ai对象的内存布局