Atomic 翻译成中文是原子的意思。在化学上,我们知道原子是构成一般物质的最小单位,在化学反应中是不可分割的。在我们这里 Atomic 是指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。
所以,所谓原子类说简单点就是具有原子/原子操作特征的类。
并发包 java.util.concurrent 的原子类都存放在java.util.concurrent.atomic下,如下图所示。
根据操作的数据类型,可以将JUC包中的原子类分为4类
使用原子的方式更新基本类型
使用原子的方式更新数组里的某个元素
AtomicMarkableReference是将一个boolean值作是否有更改的标记,本质就是它的版本号只有两个,true和false,
修改的时候在这两个版本号之间来回切换,这样做并不能解决ABA的问题,只是会降低ABA问题发生的几率而已
public class SolveABAByAtomicMarkableReference {
private static AtomicMarkableReference atomicMarkableReference = new AtomicMarkableReference(100, false);
public static void main(String[] args) {
Thread refT1 = new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicMarkableReference.compareAndSet(100, 101, atomicMarkableReference.isMarked(), !atomicMarkableReference.isMarked());
atomicMarkableReference.compareAndSet(101, 100, atomicMarkableReference.isMarked(), !atomicMarkableReference.isMarked());
});
Thread refT2 = new Thread(() -> {
boolean marked = atomicMarkableReference.isMarked();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean c3 = atomicMarkableReference.compareAndSet(100, 101, marked, !marked);
System.out.println(c3); // 返回true,实际应该返回false
});
refT1.start();
refT2.start();
}
}
描述: 第一个线程取到了变量 x 的值 A,然后巴拉巴拉干别的事,总之就是只拿到了变量 x 的值 A。这段时间内第二个线程也取到了变量 x 的值 A,然后把变量 x 的值改为 B,然后巴拉巴拉干别的事,最后又把变量 x 的值变为 A (相当于还原了)。在这之后第一个线程终于进行了变量 x 的操作,但是此时变量 x 的值还是 A,所以 compareAndSet 操作是成功。
例子描述(可能不太合适,但好理解): 年初,现金为零,然后通过正常劳动赚了三百万,之后正常消费了(比如买房子)三百万。年末,虽然现金零收入(可能变成其他形式了),但是赚了钱是事实,还是得交税的!
如图所示,线程A进行CAS逻辑,在从内存中获取到var值到开始进行逻辑之间(对应图中笑脸处),会有一个时间差;如果刚好在这个时间差内,有其他某线程对var做了一系列的操作,但最后又恢复了var的值,即:出现“偷梁换柱”的情况;虽然此时线程A仍然能CAS成功,但是中间多出的那些过程仍然可能引发问题。
我们可以通过加一个标识来解决这个问题;即:所有要用到某一共享引用的线程,如果在用到该引用时,都对该引用对应的标识进行推进的话,那么CAS在进行“上图中笑脸前笑脸后”对比时,除了要对比原来的引用(上图中的A)外,还要对比标识的值是否一样,这样就解决了ABA问题。