一.概述
java.util.concurrent.atomic
包为我们提供了一系列线程安全,用法简单的用于更新变量的原子操作类。原子类基于CAS算法实现线程安全,无需加锁,性能高效。
以AtomicInteger原子类与int类进行效果比较:
package package05_atomic;
import java.util.concurrent.atomic.AtomicInteger;
class Compare {
//默认初始值为1
private AtomicInteger AI = new AtomicInteger();
private void addValue() {
for (int i = 0; i < 5; i++) {
int value = AI.addAndGet(1);
System.out.println(Thread.currentThread().getName()+":"+value);
}
}
public static void main(String[] args) {
Compare demo = new Compare();
new Thread(demo::addValue,"线程一").start();
new Thread(demo::addValue,"线程二").start();
}
}
package package05_atomic;
class Compare {
private Integer value = 1;
private void addValue() {
for (int i = 0; i < 5; i++) {
++value;
System.out.println(Thread.currentThread().getName()+":"+value);
}
}
public static void main(String[] args) {
Compare demo = new Compare();
new Thread(demo::addValue,"线程一").start();
new Thread(demo::addValue,"线程二").start();
}
}
在未使用原子类的时候结果出现了混乱。这是由于普通的数据类型无法保证++i
等操作的原子性,从而出现混乱。
二.常见原子类
原子操作类可分为五种类型:基本数据类型原子类、数组类型原子类、引用类型原子类、对象的属性修改类型、高性能原子类。
(1)基本数据类型原子类
以AtomicInteger
为例:
package package05_atomic;
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntegerTest {
public static void main(String[] args) {
AtomicInteger i = new AtomicInteger(0);
// 获取并自增
System.out.println(i.getAndIncrement());
// 自增并获取
System.out.println(i.incrementAndGet());
// 获取并加值
System.out.println(i.getAndAdd(5));
// 加值并获取
System.out.println(i.addAndGet(-5));
// 获取并更新
System.out.println(i.getAndUpdate(p -> p - 2));
// 更新并获取
System.out.println(i.updateAndGet(p -> p + 2));
// 获取并计算
System.out.println(i.getAndAccumulate(10, (p, x) -> p + x));
// 计算并获取
System.out.println(i.accumulateAndGet(-10, (p, x) -> p + x));
}
}
(2)数组类型原子类
以AtomicIntegerArray
为例:
public final int get(int i) //获取 index=i 位置元素的值
public final int getAndSet(int i, int newValue)//返回 index=i 位置的当前的值,并将其设置为新值:newValue
public final int getAndIncrement(int i)//获取 index=i 位置元素的值,并让该位置的元素自增
public final int getAndDecrement(int i) //获取 index=i 位置元素的值,并让该位置的元素自减
public final int getAndAdd(int delta) //获取 index=i 位置元素的值,并加上预期的值
boolean compareAndSet(int expect, int update) //如果输入的数值等于预期值)(比较的是引用地址),则以原子方式元素值设置为输入值
public final void lazySet(int i, int newValue)//最终 将index=i 位置的元素设置为newValue,使用 lazySet 设置之后可能导致其他线程在之后的一小段时间内还是可以读到旧的值。
(3)引用类型原子类
引用类型原子类用于原子更新多个变量(对象)。以AtomicReference
为例:
package package05_atomic;
import java.util.concurrent.atomic.AtomicReference;
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
class AtomicReferenceTest {
public static void main(String[] args) {
AtomicReference<Person> atomicReference = new AtomicReference<Person>();
Person person = new Person("曹孟德",62);
atomicReference.set(person);
System.out.println(atomicReference.get().toString());
Person newperson = new Person("郭奉孝",37);
atomicReference.compareAndSet(person,newperson);
System.out.println(atomicReference.get().toString());
}
}
注
:在对多个变量进行原子赋值的时候可能会出现ABA问题:
解决办法是在每一个线程对变量进行赋值操作时加上版本号信息。
(4)对象的属性修改类型
该类型用于原子更新某个类里的某个字段。
以AtomicReferenceFieldUpdate
为例:
package package05_atomic;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
class Student {
public volatile String name;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
}
class FieldUpdate {
public static void main(String[] args) {
Student student = new Student();
AtomicReferenceFieldUpdater arfu = AtomicReferenceFieldUpdater.newUpdater(Student.class, String.class, "name");
arfu.compareAndSet(student, null, "诸葛孔明");
System.out.println(student);
}
}
注意
:对象的属性修改类型都是抽象类,所以在使用之前必须使用静态方法newUpdater创建一个新的更新器,然后参数传入需要更新的类和属性,属性名。同时待修改的属性必须用public volatile关键字修饰。
(5)高性能原子类(原子累加器)
原子类型累加器有如下四种:
DoubleAccumulator
DoubleAdder
LongAccumulator
LongAdder
以LongAdder为例,引入的目的是解决高并发环境下AtomicLong的自旋瓶颈问题。思想概述
:采用分段的思想,将value值分散到一个数组中,不同线程会命中到数组的不同槽中,各个线程只对自己槽中的那个值进行CAS操作,最后再把这些段的值相加得到最终的值。