1、Atomic中存在Atmomicxxx的类,都是通过CAS来实现原子性的。
对于平时适用count++问题,count++并不是线程安全的,所以在多线程情况下,适用count++会出现得到的值并不是我们期望的值。
问题如下:
所以为了解决此类问题我们需要用到Atomic,例如我们可以适用AtomicInteger来代替count++操作,保证线程安全。
例子如下:
/**
* @author v_vllchen
*/
public class AtomicExample {
private static final Logger LOGGER = LoggerFactory.getLogger(AtomicExample.class);
// 请求总数
public static int clientTotal = 5000;
// 同时并发执行的线程数
public static int threadTotal = 200;
public static int count2 = 0;
public static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal ; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
add();
semaphore.release();
} catch (Exception e) {
e.printStackTrace();
LOGGER.error("exception", e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
System.out.println("count:{}"+count.get());
LOGGER.info("count:{}", count.get());
}
private static void add() {
count.incrementAndGet();
}
}
代码来自https://blog.csdn.net/qq_34871626/article/details/81411815
在该代码中我们可以看到我们请求的总数是5000,并发执行的线程数为200,通过累加对AtomicInteger进行累加可以看到最终的结果是5000;
以上我们可以看到适用Atomic的确解决了count++的线程安全问题,但是底层如何通过CAS来实现的这样一个过程呢,还是要通过源码来解释
1. 在add()方法中有一个incrementAndGet方法,我们可以进入这个源码内部
看到它调用了unsafe类下面的getAndAddInt方法,这个方法中有三个参数,第一个参数是对象本身、第二个参数是valueOffset用来记录当前value在内存中的编译地址,第三个参数为常量
2. 继续深入查看unsafe中的getAndAddInt方法,
从第一个步中我们知道了var1是对象本身,var2是value在内存中的编译地址,var4是一个常数。 该方法中有两个方法:
1、getIntVolatile(),该方法通过var1以及var2来找到内存中的值,该方法是一个native方法,并不是Java实现的。
2、compareAndSwapInt(),在方法中通过while语句来实现compareAndSwapInt()方法,该方法是用来比较var1与getIntVolatile()底层返回的value,如果相同则把var5更新为var5+var4,如果不相同,则循环通过getIntVolatile()获取底层的value值,直到当前的var1与底层的返回的value相同才做更新。
compareAndSwapInt()方法的核心就是通常所说的CAS。
以上就是Atomic的实现思想
但是对于CAS来说会遇到一个经常性的问题就是ABA问题(线程1将值A改成B,接着又改回了A,其它线程通过与值做比较时发现值没有改变,则直接进行更改,实际上值已经被更改了)。
所以对于这种问题,在Atomic中通过AtomicStampReference类来解决ABA问题。ABA问题的解决办法就是在线程每次去更新的时候将版本号加一,这样其它线程通过版本号检测该值是否被更新过。
public class AtomicStampedReference<V> {
private static class Pair<T> {
final T reference;
final int stamp;
private Pair(T reference, int stamp) {
this.reference = reference;
this.stamp = stamp;
}
static <T> Pair<T> of(T reference, int stamp) {
return new Pair<T>(reference, stamp);
}
}
... 此处省略多个方法 ....
public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
Pair<V> current = pair;
return
expectedReference == current.reference &&
expectedStamp == current.stamp &&
((newReference == current.reference &&
newStamp == current.stamp) ||
casPair(current, Pair.of(newReference, newStamp)));
}
}