【JUC】原子操作类
文章目录
- 【JUC】原子操作类
- 1. 原子操作类
- 1.1 基本类型原子类
- 1.2 数组类型原子类
- 1.3 引用类型原子类
- 1.3.1 AtomicReference
- 1.3.2 AtomicStampedReference
- 1.3.3 AtomicMarkableReference
- 1.4 对象的属性修改原子类
1. 原子操作类
原子操作类如下所示:
- AtomicBoolean
- AtomicInteger
- AtomicIntegerArray
- AtomicIntegerFieldUpdater
- AtomicLong
- AtomicLongArray
- AtomicLongFieldUpdater
- AtomicMarkableReference
- AtomicReference
- AtomicReferenceArray
- AtomicReferenceFieldUpdater
- AtomicStampedReference
- DoubleAccumulator
- DoubleAdder
- LongAccumulator
- LongAdder
根据每个类的特点可以将其分成几种不同的类型:
1.1 基本类型原子类
基本类型原子类包含以下三类:
- AtomicInteger
- AtomicBoolean
- AtomicLong
以 AtomicInteger
为例,其常用API如下(其他类也差不多):
- public final int get() //获取当前的值
- public final int getAndSet(int newValue)//获取当前的值,并设置新的值
- public final int getAndIncrement()//获取当前的值,并自增
- public final int getAndDecrement() //获取当前的值,并自减
- public final int getAndAdd(int delta) //获取当前的值,并加上预期的值
- boolean compareAndSet(int expect, int update) //如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update)
以 AtomicInteger
演示 i++
:
class MyNumber {
AtomicInteger atomicInteger = new AtomicInteger();
public void addPlusPlus() {
atomicInteger.getAndIncrement();
}
}
public class AtomicIntegerDemo {
public static final int SIZE = 50;
public static void main(String[] args) throws InterruptedException {
MyNumber myNumber = new MyNumber();
CountDownLatch countDownLatch = new CountDownLatch(SIZE);
for (int i = 1; i <= SIZE; i++) {
new Thread(() -> {
try {
for (int j = 1; j <= 1000; j++) {
myNumber.addPlusPlus();
}
} finally {
countDownLatch.countDown();
}
}, String.valueOf(i)).start();
}
//等待上面50个线程全部计算完成后,再去获得最终值
countDownLatch.await();
System.out.println(Thread.currentThread().getName() + "\t" + "result: " + myNumber.atomicInteger.get());
}
}
运行结果如下:
1.2 数组类型原子类
数组类型原子类包含以下三类:
- AtomicIntegerArray
- AtomicLongArray
- AtomicReferenceArray
以 AtomicIntegerArray
演示基本用法:
public class AtomicIntegerArrayDemo {
public static void main(String[] args) {
AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[5]);
//AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(5);
//AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[]{1,2,3,4,5});
for (int i = 0; i < atomicIntegerArray.length(); i++) {
System.out.println(atomicIntegerArray.get(i));
}
System.out.println();
int tmpInt = 0;
tmpInt = atomicIntegerArray.getAndSet(0, 1122);
System.out.println(tmpInt + "\t" + atomicIntegerArray.get(0));
tmpInt = atomicIntegerArray.getAndIncrement(0);
System.out.println(tmpInt + "\t" + atomicIntegerArray.get(0));
}
}
运行结果如下所示:
1.3 引用类型原子类
引用类型原子类包括以下三类:
- AtomicReference
- AtomicStampedReference
- 携带版本号的引用类型原子类,可以解决
ABA问题
- 解决修改过几次
- 状态戳原子引用
- 携带版本号的引用类型原子类,可以解决
- AtomicMarkableReference
- 原子更新带有标记位的引用类型对象
- 解决是否修改过(将状态戳简化为true/false)
- 状态戳(true/false)原子引用
1.3.1 AtomicReference
AtomicReference
使用实例:
class User {
String userName;
int age;
}
/**
* @auther zzyy
* @create 2022-02-24 14:50
*/
public class AtomicReferenceDemo {
public static void main(String[] args) {
AtomicReference<User> atomicReference = new AtomicReference<>();
User z3 = new User("z3", 22);
User li4 = new User("li4", 28);
atomicReference.set(z3);
System.out.println(atomicReference.compareAndSet(z3, li4) + "\t" + atomicReference.get().toString());
System.out.println(atomicReference.compareAndSet(z3, li4) + "\t" + atomicReference.get().toString());
}
}
运行结果如下:
使用 AtomicReference
实现一个自旋锁:
public class SpinLockDemo {
AtomicReference<Thread> atomicReference = new AtomicReference<>();
public void lock() {
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() + "\t" + "----come in");
while (!atomicReference.compareAndSet(null, thread)) {
}
}
public void unLock() {
Thread thread = Thread.currentThread();
atomicReference.compareAndSet(thread, null);
System.out.println(Thread.currentThread().getName() + "\t" + "----task over,unLock...");
}
public static void main(String[] args) {
SpinLockDemo spinLockDemo = new SpinLockDemo();
new Thread(() -> {
spinLockDemo.lock();
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
spinLockDemo.unLock();
}, "A").start();
//暂停500毫秒,线程A先于B启动
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
spinLockDemo.lock();
spinLockDemo.unLock();
}, "B").start();
}
}
1.3.2 AtomicStampedReference
携带版本号的引用类型原子类,可以解决 ABA问题
。可以记录修改过几次变量。
示例如下:
public class ABADemo {
static AtomicStampedReference<Integer> stampedReference = new AtomicStampedReference<>(100, 1);
public static void main(String[] args) {
new Thread(() -> {
int stamp = stampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + "\t" + "首次版本号:" + stamp);
//暂停500毫秒,保证后面的t4线程初始化拿到的版本号和我一样
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
stampedReference.compareAndSet(100, 101, stampedReference.getStamp(), stampedReference.getStamp() + 1);
System.out.println(Thread.currentThread().getName() + "\t" + "2次流水号:" + stampedReference.getStamp());
stampedReference.compareAndSet(101, 100, stampedReference.getStamp(), stampedReference.getStamp() + 1);
System.out.println(Thread.currentThread().getName() + "\t" + "3次流水号:" + stampedReference.getStamp());
}, "t3").start();
new Thread(() -> {
int stamp = stampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + "\t" + "首次版本号:" + stamp);
//暂停1秒钟线程,等待上面的t3线程,发生了ABA问题
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean b = stampedReference.compareAndSet(100, 2022, stamp, stamp + 1);
System.out.println(b + "\t" + stampedReference.getReference() + "\t" + stampedReference.getStamp());
}, "t4").start();
}
}
运行结果如下:
1.3.3 AtomicMarkableReference
原子更新带有标记位的引用类型对象,它的定义就算将状态戳简化为 true/false
。
示例代码:
public class AtomicMarkableReferenceDemo {
static AtomicMarkableReference markableReference = new AtomicMarkableReference(100, false);
public static void main(String[] args) {
new Thread(() -> {
boolean marked = markableReference.isMarked();
System.out.println(Thread.currentThread().getName() + "\t" + "默认标识:" + marked);
//暂停1秒钟线程,等待后面的T2线程和我拿到一样的模式flag标识,都是false
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
markableReference.compareAndSet(100, 1000, marked, !marked);
}, "t1").start();
new Thread(() -> {
boolean marked = markableReference.isMarked();
System.out.println(Thread.currentThread().getName() + "\t" + "默认标识:" + marked);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean b = markableReference.compareAndSet(100, 2000, marked, !marked);
System.out.println(Thread.currentThread().getName() + "\t" + "t2线程CASresult: " + b);
System.out.println(Thread.currentThread().getName() + "\t" + markableReference.isMarked());
System.out.println(Thread.currentThread().getName() + "\t" + markableReference.getReference());
}, "t2").start();
}
}
运行结果: