并发编程之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 {
@Test
public 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 {
@Test
public 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 {
@Test
public 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
@EqualsAndHashCode
public static class Person {
private final String name;
@Override
public 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 {
@Test
public 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 {
@Test
public void test() {
// 创建有10个元素的数组
AtomicIntegerArray atomic = new AtomicIntegerArray(10);
atomic.set(0, 1); // 第0个位置设置为1
atomic.get(0); // 获取第0个位置的值。
atomic.getAndAdd(2, 1); // 获取第2个位置的值,数组中的值+1
atomic.compareAndSet(0, 1, 2); // 第0个位置的值如果时1的话,修改为2
}
}