同步数据结构之原子标量类
引言
通过原子类序章我们知道Java并发包提供的原子类共分5类,这里开始介绍第一类标量类,其实也就是原子更新基本类型和引用类型,它们是:AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference. 它们提供的方法基本相同,其中AtomicBoolean最简单,其它三个提供的方法复杂度相当,这里我先以最常使用的AtomicInteger为例进行分析。
AtomicInteger
除了在序章中提到的方法之外,AtomicInteger主要由以下四类方法(我按照自己的理解取名划分的)构成对原子变量的更新操作。
1. 简单自更新
就是指没有外部变量参与的进行简单自身加减1的操作,这类方法包括如下几个方法:
- int getAndIncrement(),以原子的方式将当前值加1,返回自增前的值;
- int getAndDecrement(),以原子的方式将当前值减1,返回自减前的值;
- int incrementAndGet(),以原子的方式将当前值加1,返回自增后的值;
- int decrementAndGet(),以原子的方式将当前值减1,返回自减后的值;
以下面getAndIncrement的源码为例,可以看出这类操作主要是利用了CAS+Volatile关键字的方式实现。
1 public final int getAndIncrement() { 2 return unsafe.getAndAddInt(this, valueOffset, 1); 3 } 4 5 //Unsafe: 6 public final int getAndAddInt(Object o, long offset, int delta) { 7 int v; 8 do { 9 v = getIntVolatile(o, offset); 10 } while (!compareAndSwapInt(o, offset, v, v + delta)); 11 return v; 12 }
就是指有外部简单变量参与的对自身进行更新的操作,这类方法包括如下几个,当然在序章中介绍的那几个基本方法set、lazySet、compareAndSet、weakCompareAndSet也属于此类方法。
- int getAndSet(int newValue),以原子的方式设置成新值newValue,并返回旧值;
- int getAndAdd(int delta),以原子的方式将实例中的值(AtomicInteger里的value)增加delta,但是返回增加前的旧值;
- int addAndGet(int delta),以原子的方式将实例中的值(AtomicInteger里的value)增加delta,返回增加后的新值;
1 public final int getAndUpdate(IntUnaryOperator updateFunction) { 2 int prev, next; 3 do { 4 prev = get(); 5 next = updateFunction.applyAsInt(prev); 6 } while (!compareAndSet(prev, next)); 7 return prev; 8 } 9 10 public final int updateAndGet(IntUnaryOperator updateFunction) { 11 int prev, next; 12 do { 13 prev = get(); 14 next = updateFunction.applyAsInt(prev); 15 } while (!compareAndSet(prev, next)); 16 return next; 17 }
从表面上看,以上两个方法看不出函数式编程的影子,只是传入了一个IntUnaryOperator变量,执行了它的applyAsInt方法,我们接着看IntUnaryOperator的源码:
1 @FunctionalInterface 2 public interface IntUnaryOperator { 3 4 int applyAsInt(int operand); 5 6 default IntUnaryOperator compose(IntUnaryOperator before) { 7 Objects.requireNonNull(before); 8 return (int v) -> applyAsInt(before.applyAsInt(v)); 9 } 10 11 default IntUnaryOperator andThen(IntUnaryOperator after) { 12 Objects.requireNonNull(after); 13 return (int t) -> after.applyAsInt(applyAsInt(t)); 14 } 15 16 static IntUnaryOperator identity() { 17 return t -> t; 18 }
1 public static void main(String[] args) { 2 IntUnaryOperator add = new IntUnaryOperator(){ 3 4 @Override 5 public int applyAsInt(int operand) { 6 return operand + operand; 7 } 8 }; 9 10 IntUnaryOperator mul = new IntUnaryOperator(){ 11 12 @Override 13 public int applyAsInt(int operand) { 14 return operand * operand; 15 } 16 }; 17 18 int i = new AtomicInteger(3).updateAndGet(add.andThen(mul)); 19 int j = new AtomicInteger(3).updateAndGet(add.compose(mul)); 20 System.out.println(i);// 36 21 System.out.println(j);// 18 22 }
1 @Override 2 public IntUnaryOperator andThen(IntUnaryOperator after) { 3 4 //return (int t) -> after.applyAsInt(applyAsInt(t)); 5 return new IntUnaryOperator(){ 6 7 @Override 8 public int applyAsInt(int t) { 9 return after.applyAsInt(applyAsInt(t)); 10 } 11 12 }; 13 }
1 public final int getAndAccumulate(int x,IntBinaryOperator accumulatorFunction) { 2 int prev, next; 3 do { 4 prev = get(); 5 next = accumulatorFunction.applyAsInt(prev, x); 6 } while (!compareAndSet(prev, next)); 7 return prev; 8 } 9 10 public final int accumulateAndGet(int x,IntBinaryOperator accumulatorFunction) { 11 int prev, next; 12 do { 13 prev = get(); 14 next = accumulatorFunction.applyAsInt(prev, x); 15 } while (!compareAndSet(prev, next)); 16 return next; 17 }
从以上源码可以看出,它接受一个外部变量x参与运算,具体的运算逻辑由第二个IntBinaryOperator类型的参数的applyAsInt方法实现,我们接着看IntBinaryOperator的源码:
1 @FunctionalInterface 2 public interface IntBinaryOperator { 3 4 int applyAsInt(int left, int right); 5 }
IntBinaryOperator是很简单的接口,有且仅有一个实现真正的运算逻辑的方法接口方法,在AtomicInteger的这两个方法中就是以旧值和传入的参数(x)进行实现运算逻辑。我们以实现x的y次方为例:
1 public static void main(String[] args) { 2 3 IntBinaryOperator pow = new IntBinaryOperator(){ 4 5 @Override 6 public int applyAsInt(int left, int right) { 7 return (int) Math.pow(left, right); 8 } 9 10 }; 11 12 System.out.println(new AtomicInteger(2).accumulateAndGet(10, pow)); //1024 13 }
AtomicReference
1. 直接更新
1 public final V getAndSet(V newValue) { 2 return (V)unsafe.getAndSetObject(this, valueOffset, newValue); 3 }
2. 函数式自更新
与AtomicInteger的函数式自更新类似,在不借助外部变量的情况下,仅根据引用变量自身进行逻辑运算并更新,它对应的方法分别是getAndUpdate/updateAndGet依然只是返回值是旧值或新值的区别:
1 public final V getAndUpdate(UnaryOperator<V> updateFunction) { 2 V prev, next; 3 do { 4 prev = get(); 5 next = updateFunction.apply(prev); 6 } while (!compareAndSet(prev, next)); 7 return prev; 8 } 9 10 public final V updateAndGet(UnaryOperator<V> updateFunction) { 11 V prev, next; 12 do { 13 prev = get(); 14 next = updateFunction.apply(prev); 15 } while (!compareAndSet(prev, next)); 16 return next; 17 }
UnaryOperator依然是一个接口类,apply是待实现的接口方法,它的其他方法与IntUnaryOperator非常类似也是compose、andThen两个对执行顺序控制的方法,这里就不再提供源码。
1 public static void main(String[] args) { 2 Person p0 = new Person(10, "Tom"); //age, name 3 4 AtomicReference<Person> ar = new AtomicReference<Person>(p0); 5 6 UnaryOperator<Person> a = new UnaryOperator<Person>(){ 7 8 @Override 9 public Person apply(Person t) { 10 11 if(t.getAge() == 10){ 12 t.setAge(11); 13 t.setName("Tom11"); 14 } 15 return t; 16 } 17 18 }; 19 System.out.println(ar.updateAndGet(a).toString()); 20 }
3. 函数式外更新
1 public final V getAndAccumulate(V x,BinaryOperator<V> accumulatorFunction) { 2 V prev, next; 3 do { 4 prev = get(); 5 next = accumulatorFunction.apply(prev, x); 6 } while (!compareAndSet(prev, next)); 7 return prev; 8 } 9 10 public final V accumulateAndGet(V x,BinaryOperator<V> accumulatorFunction) { 11 V prev, next; 12 do { 13 prev = get(); 14 next = accumulatorFunction.apply(prev, x); 15 } while (!compareAndSet(prev, next)); 16 return next; 17 }
BinaryOperator接口类需要实现的方法是其父接口BiFunction中的接口方法apply,除了从父接口BiFunction中继承了andThen方法之外,它自身还有两个静态方法minBy和maxBy分别比较两个对象返回最小值和最大值:
1 public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) { 2 Objects.requireNonNull(comparator); 3 return (a, b) -> comparator.compare(a, b) <= 0 ? a : b; 4 } 5 6 public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) { 7 Objects.requireNonNull(comparator); 8 return (a, b) -> comparator.compare(a, b) >= 0 ? a : b; 9 }
相关的示例如下:
1 public static void main(String[] args) { 2 Person p0 = new Person(10, "Tom"); 3 Person p1 = new Person(12, "Tom12"); 4 AtomicReference<Person> ar = new AtomicReference<Person>(p0); 5 AtomicReference<Person> ar1 = new AtomicReference<Person>(p0); 6 7 BinaryOperator<Person> b = new BinaryOperator<Person>(){ 8 9 @Override 10 public Person apply(Person t, Person u) { 11 if(t.getAge() == 10){ 12 return new Person(11, "Tom11"); 13 } 14 return t; 15 } 16 }; 17 18 System.out.println(ar.accumulateAndGet(p1, b)); //ar更新为新的Person对象:age为11,name为Tome11 19 20 System.out.println(ar.accumulateAndGet(p1, BinaryOperator.maxBy(new Comparator<Person>() { 21 22 @Override 23 public int compare(Person o1, Person o2) { 24 return o1.getAge() - o2.getAge(); 25 } 26 27 }))); //设置并返回年龄最大的对象P1:age为12,name为Tome12,其实就是如果p1比ar对应的Person实例大才更新为新值p1,否则还是自己。 28 29 System.out.println(ar1.accumulateAndGet(p1, BinaryOperator.minBy(new Comparator<Person>() { 30 31 @Override 32 public int compare(Person o1, Person o2) { 33 return o1.getAge() - o2.getAge(); 34 } 35 36 })));//设置并返回年龄最小的对象P0:age为10,name为Tom,其实就是如果p1比ar1对应的p0小才更新为新值p1,否则还是自己。 37 }

浙公网安备 33010602011771号