什么是原子类
以前认为原子是不可分割的最小单位。故原子类可以认为其操作时不可分割的。
为什么要有原子类
对多线程访问同一变量,我们需要加锁,而锁是比较消耗性能的,jdk1.5之后,新增的原子操作类提供了一种简单、性能高效、线程安全地更新一个变量的方式,这些类同样位于juc包下的atomic包下,发展到jdk1.8,该报共有17个类,囊括了原子更新基本类型、原子更新数组、原子更新新属性、原子更新引用。
1.8新增的原子类
DoubleAccumulator、DoubleAdder、LongAccumulator、LongAdder、LongAccumulator、LongAdder
大致可归为三类
AtomicBoolean、AtomicIndteger、AutomicLong 元老级的原子更新,方法几乎一模一样
DoubleAdder、LongAdder 对Double、Long的原子更新性能进行优化提升
DoubleAccumulator、LongAccumlator 支持自定义运算
原子更新数组类型
AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
原子的更新属性
原子的更新某个类里的字段时,需要使用原子更新字段类,AtomicStampedReference、AtomicReference、FiledUpdater
使用上述类是必须遵循以下原则
字段必须是voliatile类型的,在线程之间共享变量时能保持可见性。
字段的描述类型是与调用者的操作对象字段保持一致。
也就是说调用者可以直接操作对象字段,那么就可以反射进行原子操作。
对父类的字段,子类不能直接操作的,尽管子类可以访问父类的字段
只能是实例变量,不能是类变量,也就是说不能加static关键字
只能是可修改变量,不能使用final修饰变量,final的语义,不可更改。
对于AtomicIntegerFieldUpdater和AtomicLongFieldUpdater只能修改int/long类型的字段,不能修改其包装类型 (Integer/Long)
如果需要修改包装类型就需要使用AtomicReferenceFieldUpdater。
下面写几个原子类使用的简单例子
1.AtomicIndteger实现一个线程安全的自增
import java.util.concurrent.atomic.AtomicInteger;
/**
* AtomicInteger demo
*/
public class AtomicIntegerDemo {
private static AtomicInteger sum = new AtomicInteger(0);
public static void increase(){
sum.incrementAndGet();
}
public static void main(String[] args) throws InterruptedException{
for (int i=0;i<10;i++){
new Thread(()->{
for (int j=0;j<10;j++){
increase();
System.out.println(sum);
}
}).start();
}
}
}
2.LongAccumulator Long类型的原子运算
import java.util.concurrent.atomic.LongAccumulator;
public class LongAccumulatorDemo {
public static void main(String[] args) {
LongAccumulator longAccumulator = new LongAccumulator((left,right)->
left<right?right:left,0l);
longAccumulator.accumulate(3);
System.out.println(longAccumulator.get());
longAccumulator.accumulate(6);
System.out.println(longAccumulator.get());
}
}
运行结果
3.AtomicIntegerArray 原子更新int数组元素
import java.util.concurrent.atomic.AtomicIntegerArray;
public class AtomicIntegerArrayDemo {
public static void main(String[] args) {
int[] arr = new int[]{3,2};
AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(arr);
System.out.println("arr数组下标为1的元素的值加3等于:"
+atomicIntegerArray.addAndGet(1, 3));
System.out.println("arr数组下标为0的元素的值同2比较,较大的值为:"
+atomicIntegerArray.accumulateAndGet(0, 2, ((left, right) -> left>right?left:right)));
}
}
4.原子更新属性,先创建一个简单的Student类,方便后面的测试
public class Student {
volatile long id;
volatile String name;
public Student(Long id, String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
4.1AtomicLongFieldUpdater 更新long类型属性 ,AtomicReferenceFieldUpdater 更新String类型属性
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
public class AtomicFieldUpdaterDemo {
public static void main(String[] args) {
//更新long类型属性
AtomicLongFieldUpdater<Student> atomicLongFieldUpdater = AtomicLongFieldUpdater.newUpdater(Student.class, "id");
Student student = new Student(1L,"lisi");
atomicLongFieldUpdater.compareAndSet(student, 1L, 101l);
System.out.println("更新long类型的属性id:"+student.getId());
//更新包装类型属性
AtomicReferenceFieldUpdater<Student,String> atomicReferenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(Student.class, String.class,"name" );
atomicReferenceFieldUpdater.compareAndSet(student, "lisi", "zhangsan");
System.out.println("更新String类型的属性name:"+student.getName());
}
}
运行结果: