一、CAS简介
CAS(Compare And Swap)是由硬件实现的.CAS 可以将read- modify - write 这类的操作转换为原子操作.i++自增操作包括三个子操作:
- 从主内存读取i 变量值
- 对i 的值加1
- 再把加1 之后的值保存到主内存
CAS 原理: 在把数据更新到主内存时,再次读取主内存变量的值,如果现在变量的值与期望的值(操作起始时读取的值)一样就更新.

使用CAS实现线程安全的计数器
public class CASTest { public static void main(String[] args) { CASCounter casCounter = new CASCounter(); for (int i = 0; i < 100000; i++) { new Thread(new Runnable() { @Override public void run() { System.out.println(casCounter.incrementAndGet()); } }).start(); } } } class CASCounter { // 使用volatile 修饰value 值,使线程可见 volatile private long value; public long getValue() { return value; } // 定义comare and swap 方法 private boolean compareAndSwap(long expectedValue, long newValue) { // 如果当前value 的值与期望的expectedVAlue 值一样,就把当前的Value 字段替换为newValue 值 synchronized (this) { if (value == expectedValue) { value = newValue; return true; } else { return false; } } } // 定义自增的方法 public long incrementAndGet() { long oldvalue; long newValue; do { oldvalue = value; newValue = oldvalue + 1; } while (!compareAndSwap(oldvalue, newValue)); return newValue; } }
CAS 实现原子操作背后有一个假设: 共享变量的当前值与当前线程提供的期望值相同, 就认为这个变量没有被其他线程修改过.
实际上这种假设不一定总是成立.如有共享变量count = 0,
A 线程对count 值修改为10
B 线程对count 值修改为20
C 线程对count 值修改为0
当前线程看到count 变量的值现在是0,现在是否认为count 变量的值没有被其他线程更新呢? 这种结果是否能够接受??
这就是CAS 中的ABA 问题,即共享变量经历了A->B->A 的更新.是否能够接收ABA 问题跟实现的算法有关.如果想要规避ABA 问题,可以为共享变量引入一个修订号(时间戳), 每次修改共享变量时,相应的修订号就会增加1. ABA 变量更新过程变量: [A,0] ->[B,1]->[A,2], 每次对共享变量的修改都会导致修订号的增加,通过修订号依然可以准确判断变量是否被其他线程修改过. AtomicStampedReference 类就是基于这种思想产生的.
二、原子变量类
原子变量类基于CAS 实现的, 当对共享变量进行read-modify-write更新操作时,通过原子变量类可以保障操作的原子性与可见性.对变量的read-modify-write 更新操作是指当前操作不是一个简单的赋值,而是变量的新值依赖变量的旧值,如自增操作i++. 由于volatile 只能保证可见性,无法保障原子性, 原子变量类内部就是借助一个Volatile 变量,
并且保障了该变量的read-modify-write 操作的原子性, 有时把原子变量类看作增强的volatile 变量. 原子变量类有12 个,如:
| 分组 | 原子变量类 |
| 基础数据型 | AtomicInteger, AtomicLong, AtomicBoolean |
| 数组型 |
AtomicIntegerArray, |
| 字段更新器 | AtomicIntegerFieldUpdater,AtomicLongFieldUpdater,
AtomicReferenceFieldUpdater |
| 引用型 |
AtomicReference, AtomicStampedReference, |
三、原子数组AtomicIntegerArray
public static void main(String[] args) { // 1)创建一个指定长度的原子数组 AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(10); System.out.println(atomicIntegerArray); // [0, 0, 0, 0, 0, 0, 0, 0, 0, // 0] // 2)返回指定位置的元素 System.out.println(atomicIntegerArray.get(0)); // 0 System.out.println(atomicIntegerArray.get(1)); // 0 // 3)设置指定位置的元素 atomicIntegerArray.set(0, 10); // 在设置数组元素的新值时, 同时返回数组元素的旧值 System.out.println(atomicIntegerArray.getAndSet(1, 11)); // 0 System.out.println(atomicIntegerArray); // [10, 11, 0, 0, 0, 0, 0, 0, 0, // 0] // 4)修改数组元素的值,把数组元素加上某个值 System.out.println(atomicIntegerArray.addAndGet(0, 22)); // 32 System.out.println(atomicIntegerArray.getAndAdd(1, 33)); // 11 System.out.println(atomicIntegerArray); // [32, 44, 0, 0, 0, 0, 0, 0, 0, // 0] // 5)CAS 操作 // 如果数组中索引值为0 的元素的值是32 , 就修改为222 System.out.println(atomicIntegerArray.compareAndSet(0, 32, 222)); // true System.out.println(atomicIntegerArray); // [222, 44, 0, 0, 0, 0, 0, 0, // 0, 0] System.out.println(atomicIntegerArray.compareAndSet(1, 11, 333)); // false System.out.println(atomicIntegerArray); // 6)自增/自减 System.out.println(atomicIntegerArray.incrementAndGet(0)); // 223, 相当于前缀 System.out.println(atomicIntegerArray.getAndIncrement(1)); // 44, 相当于后缀 System.out.println(atomicIntegerArray); // [223, 45, 0, 0, 0, 0, 0, 0, // 0, 0] System.out.println(atomicIntegerArray.decrementAndGet(2)); // -1 System.out.println(atomicIntegerArray); // [223, 45, -1, 0, 0, 0, 0, 0, // 0, 0] System.out.println(atomicIntegerArray.getAndDecrement(3)); // 0 System.out.println(atomicIntegerArray); // [223, 45, -1, -1, 0, 0, 0, 0, // 0, 0] }
四、AtomicIntegerFieldUpdater
AtomicIntegerFieldUpdater 可以对原子整数字段进行更新,要求:
- 字符必须使用volatile 修饰,使线程之间可见
- 只能是实例变量,不能是静态变量,也不能使用final 修饰
public class User { int id; volatile int age; public User(int id, int age) { this.id = id; this.age = age; } @Override public String toString() { return "User{" + "id=" + id + ", age=" + age + '}'; } }
public class SubThread extends Thread { private User user; // 要更新的User 对象 // 创建AtomicIntegerFieldUpdater 更新器 private AtomicIntegerFieldUpdater<User> updater = AtomicIntegerFieldUpdater .newUpdater(User.class, "age"); public SubThread(User user) { this.user = user; } @Override public void run() { // 在子线程中对user 对象的age 字段自增10 次 for (int i = 0; i < 10; i++) { System.out.println(updater.getAndIncrement(user)); } } }
public static void main(String[] args) { User user = new User(1234, 10); // 开启10 个线程 for (int i = 0; i < 10; i++) { new SubThread(user).start(); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(user); }
五、AtomicReference(可以原子读写一个对象)
// 创建一个AtomicReference 对象 static AtomicReference<String> atomicReference = new AtomicReference<>("abc"); public static void main(String[] args) throws InterruptedException { // 创建100 个线程修改字符串 for (int i = 0; i < 100; i++) { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(new Random().nextInt(20)); } catch (InterruptedException e) { e.printStackTrace(); } if (atomicReference.compareAndSet("abc", "def")) { System.out.println(Thread.currentThread().getName() + "把字符串abc 更改为def"); } } }).start(); } // 再创建100 个线程 for (int i = 0; i < 100; i++) { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(new Random().nextInt(20)); } catch (InterruptedException e) { e.printStackTrace(); } if (atomicReference.compareAndSet("def", "abc")) { System.out.println(Thread.currentThread().getName() + "把字符串还原为abc"); } } }).start(); } Thread.sleep(1000); System.out.println(atomicReference.get()); }
六、AtomicStampedReference
AtomicStampedReference 原子类可以解决CAS 中的ABA 问题,在AtomicStampedReference 原子类中有一个整数标记值stamp, 每次执行CAS 操作时,需要对比它的版本,即比较stamp 的值
public class Test { // 定义AtomicStampedReference 引用操作"abc"字符串,指定初始化版本号为0 private static AtomicStampedReference<String> stampedReference = new AtomicStampedReference<>( "abc", 0); public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new Runnable() { @Override public void run() { stampedReference.compareAndSet("abc", "def", stampedReference.getStamp(), stampedReference.getStamp() + 1); System.out.println(Thread.currentThread().getName() + "--" + stampedReference.getReference()); stampedReference.compareAndSet("def", "abc", stampedReference.getStamp(), stampedReference.getStamp() + 1); } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { int stamp = stampedReference.getStamp(); // 获得版本号 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(stampedReference.compareAndSet("abc", "ggg", stamp, stamp + 1)); } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(stampedReference.getReference()); } }