并发编程之Atomic原子操作类
基本类型:AtomicInteger、AtomicBoolean、AtomicLong
引用类型:AtomicReference、AtomicMarkableReference、AtomicStampedReference
数组类型:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
对象属性原子修改器:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
原子类型累加器:DoubleAccumulator、DoubleAdder、LongAccumulator、LongAdder、Striped64
1. 基本类型
以AtomicInteger为例,它提供了原子计数器和比较交换功能
1.1 原子计数器
addAndGet()- 以原子方式将给定值添加到当前值,并在添加后返回新值。
getAndAdd() - 以原子方式将给定值添加到当前值并返回旧值。
incrementAndGet()- 以原子方式将当前值递增1并在递增后返回新值。它相当于i ++操作。
getAndIncrement() - 以原子方式递增当前值并返回旧值。它相当于++ i操作。
decrementAndGet()- 原子地将当前值减1并在减量后返回新值。它等同于i-操作。
getAndDecrement() - 以原子方式递减当前值并返回旧值。它相当于-i操作。
使用示例:
package org.example.concurrent;import lombok.extern.slf4j.Slf4j;
import org.junit.Test;import java.util.concurrent.atomic.AtomicInteger;@Slf4j
public class AtomicIntegerTest {@Testpublic void test() {AtomicInteger atomic = new AtomicInteger(0);log.debug("atomic={}", atomic);log.debug("atomic.addAndGet(1)={}, atomic={}", atomic.addAndGet(1), atomic);log.debug("atomic.getAndAdd(1)={}, atomic={}", atomic.getAndAdd(1), atomic);log.debug("atomic.incrementAndGet()={}, atomic={}", atomic.incrementAndGet(), atomic);log.debug("atomic.getAndIncrement()={}, atomic={}", atomic.getAndIncrement(), atomic);log.debug("atomic.decrementAndGet()={}, atomic={}", atomic.decrementAndGet(), atomic);log.debug("atomic.getAndDecrement()={}, atomic={}", atomic.getAndDecrement(), atomic);}
}
打印结果:
18:34:38.578 [main] DEBUG o.e.concurrent.AtomicIntegerTest - atomic=0
18:34:38.581 [main] DEBUG o.e.concurrent.AtomicIntegerTest - atomic.addAndGet(1)=1, atomic=1
18:34:38.581 [main] DEBUG o.e.concurrent.AtomicIntegerTest - atomic.getAndAdd(1)=1, atomic=2
18:34:38.581 [main] DEBUG o.e.concurrent.AtomicIntegerTest - atomic.incrementAndGet()=3, atomic=3
18:34:38.581 [main] DEBUG o.e.concurrent.AtomicIntegerTest - atomic.getAndIncrement()=3, atomic=4
18:34:38.581 [main] DEBUG o.e.concurrent.AtomicIntegerTest - atomic.decrementAndGet()=3, atomic=3
18:34:38.581 [main] DEBUG o.e.concurrent.AtomicIntegerTest - atomic.getAndDecrement()=3, atomic=2
1.2 比较和交换功能
比较和交换操作将内存中的内容与给定值进行比较,并且只有它们相同时,才将该内存位置的内容修改为给定的新值。这是作为单个原子操作完成的。
boolean compareAndSet(int expect, int update);//设置成功返回true,否则返回false
代码示例:
package org.example.concurrent;import lombok.extern.slf4j.Slf4j;
import org.junit.Test;import java.util.concurrent.atomic.AtomicInteger;@Slf4j
public class AtomicIntegerTest {@Testpublic void test() {AtomicInteger atomic = new AtomicInteger(0);log.debug("atomic={}, atomic.compareAndSet(0, 22) = {}", atomic.get(),atomic.compareAndSet(0, 22));// atomic中的值已经为22, 下面的修改会失败log.debug("atomic={}, atomic.compareAndSet(0, 22) = {}", atomic.get(),atomic.compareAndSet(0, 22));}
}
打印结果:
19:03:32.844 [main] DEBUG o.e.concurrent.AtomicIntegerTest - atomic=0, atomic.compareAndSet(0, 22) = true
19:03:32.847 [main] DEBUG o.e.concurrent.AtomicIntegerTest - atomic=22, atomic.compareAndSet(0, 22) = false
2. 引用类型
2.1 AtomicReference
原子引用可以保证你在修改引用的对象时(引用对象1改为引用对象2)的线程安全性。compareAndSet比较的是否是相同的对象,不是调用对象的equal比较。
示例:
package org.example.concurrent;import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;import java.util.concurrent.atomic.AtomicReference;@Slf4j
public class AtomicReferenceTest {@Testpublic void test() {Person person1 = new Person("张三");Person person2 = new Person("李四");AtomicReference<Person> atomic = new AtomicReference<>(person1);log.debug("atomic={}, atomic.compareAndSet({}, {}) = {}", atomic.get(), person1, person2,atomic.compareAndSet(person1, person2));log.debug("atomic={}, atomic.compareAndSet({}, {}) = {}", atomic.get(), person1, person2,atomic.compareAndSet(person1, person2));// 创建一个新的李四对象,设置仍然失败log.debug("atomic={}, atomic.compareAndSet({}, {}) = {}", atomic.get(), person2, person1,atomic.compareAndSet(new Person("李四"), person1));}@AllArgsConstructor@Getter@EqualsAndHashCodepublic static class Person {private final String name;@Overridepublic String toString() {return getName();}}
}
打印结果:
19:01:21.260 [main] DEBUG o.e.concurrent.AtomicReferenceTest - atomic=张三, atomic.compareAndSet(张三, 李四) = true
19:01:21.263 [main] DEBUG o.e.concurrent.AtomicReferenceTest - atomic=李四, atomic.compareAndSet(张三, 李四) = false
19:01:21.263 [main] DEBUG o.e.concurrent.AtomicReferenceTest - atomic=李四, atomic.compareAndSet(李四, 张三) = false
2.2 AtomicMarkableReference 带标记的原子引用
标记只能携带true和false两个值。提供的方法有:
- getReference() 获取当前引用的对象
- isMarked() 返回标记的值
-
compareAndSet(refer, newRefer, mark, newMark),引用对象和标记都相同时,重新设置引用和标记,返回更新成功/失败
-
boolean attemptMark(ref, newMark), 引用对象相同时,更新标记,返回跟新成功/失败
代码示例:
package org.example.concurrent;import lombok.extern.slf4j.Slf4j;
import org.junit.Test;import java.util.concurrent.atomic.AtomicMarkableReference;@Slf4j
public class AtomicMarkableReferenceTest {@Testpublic void test() {AtomicMarkableReference<String> atomic = new AtomicMarkableReference<>("abc", true);log.debug("reference = {}, mark={}", atomic.getReference(), atomic.isMarked());log.debug("atomic.compareAndSet(\"abc\",\"def\", false, true) = {}",atomic.compareAndSet("abc", "def", false, true));log.debug("atomic.compareAndSet(\"abc\",\"def\", false, true) = {}",atomic.compareAndSet("abc", "def", true, true));log.debug("reference = {}, mark={}", atomic.getReference(), atomic.isMarked());log.debug("atomic.attemptMark(\"def\", false) = {}, atomicMark={}",atomic.attemptMark("def", false), atomic.isMarked());}
}
执行结果:
19:48:27.319 [main] DEBUG o.e.c.AtomicMarkableReferenceTest - reference = abc, mark=true
19:48:27.322 [main] DEBUG o.e.c.AtomicMarkableReferenceTest - atomic.compareAndSet("abc","def", false, true) = false
19:48:27.322 [main] DEBUG o.e.c.AtomicMarkableReferenceTest - atomic.compareAndSet("abc","def", false, true) = true
19:48:27.322 [main] DEBUG o.e.c.AtomicMarkableReferenceTest - reference = def, mark=true
19:48:27.322 [main] DEBUG o.e.c.AtomicMarkableReferenceTest - atomic.attemptMark("def", false) = true, atomicMark=false
2.3 AtomicStampedReference带版本号的原子引用
AtomicStampedReference 和 AtomicMarkableReference类似,只不过标记由boolean改为了int,方法attemptMark变为了attemptStamp;方法isMarked变为了getStamp
3. 数组类型
AtomicIntegerArray对数组元素的操作是原子的,提供基于数组下标的get,set,compareAndSet等操作。创建时需要提供操作的数组或者指定数组的大小。
package org.example.concurrent;import lombok.extern.slf4j.Slf4j;
import org.junit.Test;import java.util.concurrent.atomic.AtomicIntegerArray;@Slf4j
public class AtomicIntegerArrayTest {@Testpublic void test() {// 创建有10个元素的数组AtomicIntegerArray atomic = new AtomicIntegerArray(10);atomic.set(0, 1); // 第0个位置设置为1atomic.get(0); // 获取第0个位置的值。atomic.getAndAdd(2, 1); // 获取第2个位置的值,数组中的值+1atomic.compareAndSet(0, 1, 2); // 第0个位置的值如果时1的话,修改为2}
}