Atomic 包提供了一种用法简单、性能高效、线程安全的方式来更新一个变量
变量的类型主要包括4中类型的原子更新方式:原子更新基本数据类型,原子更新数组,原子更新引用,原子更新属性(字段)其内部都是通过 CAS 机制来实现线程安全
原子更新基本数据类型
Atomic 包提供了三个类来更新基本数据类型:AtomicBoolean,AtomicInteger,AtomicLong,这三种方法实现的内部原理都是差不多的,就拿 AtomicInteger 来举例说明其内部原理
其常用方法 getAndIncrement() 作用是,将当前 AtomicInteger 对象中的 value 值进行 +1 并且返回的是更新之前的值,通过通过 get() 方法就能获取目前最新的值
其内部就是通过 CAS 机制来保证变量增加的线程安全,其中有三个值,内存中的值 v,旧的预期值 B ,更新后的值 A,首先会取得 AtomicInteger 中的数值,然后对其 +1 后判断,内存中的值 V 是否等于旧的预期值 B ,如果相等的话说明在这个线程对变量更新的过程中没有其他线程修改该变量的值,那么就是线程安全的,就可以将该变量的值更新,如果不同,那么说明中间被其他线程修改过了,此时就会返回 false ,然后重新进入 for 循环进行自旋
CAS机制是通过 Unsafe实现的,那么其源码中只包含了 compareAndSwapObject,compareAndSwapInteger,compareAndSwapLong 方法实现 CAS 机制
问题:flaot ,char,short 等其他基本数据类型的变量如何实现原子更新?
先把这些数据类型转成整型,然后再使用 CAS 机制更新
原子更新数组
AtomicIntegerArray:原子更新整型数组中的元素
AtomicLongArray:原子更新长整型数组中的元素
AtomicReferenceArray:原子更新引用类型数组中的元素
public class AtomicDemo {
// private static AtomicInteger atomicInteger = new AtomicInteger(1);
private static int[] value = new int[]{1, 2, 3};
private static AtomicIntegerArray integerArray = new AtomicIntegerArray(value);
public static void main(String[] args) {
//对数组中索引为1的位置的元素加5
int result = integerArray.getAndAdd(1, 5);
System.out.println(integerArray.get(1));
System.out.println(result);
}
}
输出结果:
7
2
原子更新引用类型
AtomicReference:原子更新引用类型;
AtomicReferenceFieldUpdater:原子更新引用类型里的字段;
AtomicMarkableReference:原子更新带有标记位的引用类型
public class AtomicDemo {
private static AtomicReference<User> reference = new AtomicReference<>();
public static void main(String[] args) {
User user1 = new User("a", 1);
reference.set(user1);
User user2 = new User("b",2);
User user = reference.getAndSet(user2);
System.out.println(user);
System.out.println(reference.get());
}
static class User {
private String userName;
private int age;
public User(String userName, int age) {
this.userName = userName;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", age=" + age +
'}';
}
}
}
输出结果:
User{userName='a', age=1}
User{userName='b', age=2}
原子更新字段类型
AtomicIntegeFieldUpdater:原子更新整型字段类;
AtomicLongFieldUpdater:原子更新长整型字段类;
AtomicStampedReference:原子更新引用类型,这种更新方式会带有版本号。而为什么在更新的时候会带有版本号,是为了解决CAS的ABA问题;
要想使用原子更新字段需要两步操作:
原子更新字段类都是抽象类,只能通过静态方法newUpdater来创建一个更新器,并且需要设置想要更新的类和属性;
更新类的属性必须使用public volatile进行修饰
public class AtomicDemo {
private static AtomicIntegerFieldUpdater updater = AtomicIntegerFieldUpdater.newUpdater(User.class,"age");
public static void main(String[] args) {
User user = new User("a", 1);
int oldValue = updater.getAndAdd(user, 5);
System.out.println(oldValue);
System.out.println(updater.get(user));
}
static class User {
private String userName;
public volatile int age;
public User(String userName, int age) {
this.userName = userName;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", age=" + age +
'}';
}
}
}
输出结果:
1
6