1.CAS(compare and swap) 对比交换
CAS ,意思是compare and swap,是指程序对比变量,并且交换变量的操作。 一般现代CPU 都增添了一个CAS 指令,由CPU 来保证这个CAS指令操作的原子性,当在进行操作时候,CPU 会一直循环这个指令,直到成功。 示例如图:
在JDK 里面一些如AtomicInteger等命名的变量都是利用的这种形式实现的。
2.CAS 的缺陷
CAS 也有自己的局限性:
- ABA 问题.
- 开销问题.
- 只能保证共享原子变量的更新. 当一般我们用syncnized进行同步时候,可以认为是一个原子操作.在执行命令时候,不会被其他线程抢占CPU.
在解决ABA 问题时候,解决方式是使用版本戳,在修改版本时候,加上版本号.JDK里面也有提供类似的解决方案的类.
AtomicMarkableReference和AtomicStamedReference.
这两者的区别在于前置只关心变量是否是被修改过了,后者关心变量被修改几次.
在解决只能保证共享原子变量的更新 使用AtomicReference ,索引一个变量,进行更新的原子操作.
JDK 里面实现的一些CAS 类:
3. 代码示例
package com.company.test.atomictest;
import java.util.concurrent.atomic.AtomicMarkableReference;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;
public class AtomicTest {
static AtomicStampedReference<String> reference = new AtomicStampedReference<String>("mark", 0);
static AtomicMarkableReference<String> markableReference = new AtomicMarkableReference<String>("mark", true);
static AtomicReference<User> userReference = new AtomicReference<User>();
public static void main(String[] args) {
final int oldVersion = reference.getStamp();
final String oldStr = reference.getReference();
final String markStr = markableReference.getReference();
User tom = new User("Tom", 12);
userReference.set(tom);
Thread thread1 = new Thread("java") {
@Override
public void run() {
super.run();
System.out.println(Thread.currentThread().getName() + ",current Str=" + oldStr + ",current version=" + oldVersion);
reference.compareAndSet(oldStr, oldStr + "Java", oldVersion, oldVersion + 1);
}
};
Thread thread2 = new Thread("test") {
@Override
public void run() {
super.run();
System.out.println(markableReference.compareAndSet("mark", markStr + "able", true, false)
);
}
};
Thread thread3= new Thread("test3") {
@Override
public void run() {
super.run();
User user = new User("jack", 22);
boolean b = userReference.compareAndSet(tom,user);
System.out.println(b);
}
};
thread1.start();
thread2.start();
thread3.start();
try {
Thread.sleep(8000);
System.out.println(Thread.currentThread().getName() + ",AtomicStampedReference Str=" + reference.getReference() + ",current " +
"version=" + reference.getStamp());
System.out.println(Thread.currentThread().getName() + ",markableReference Str=" + markableReference.getReference());
System.out.println(userReference.get());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
}