Java- CAS带来的 ABA问题及解决方法的代码实例
Posted 小陈乱敲代码
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java- CAS带来的 ABA问题及解决方法的代码实例相关的知识,希望对你有一定的参考价值。
1. CAS(Compare And Swap)导致的ABA问题
代码实例。
主要操作方法是 AtomicReference.compareAndSet(oldvalue, newValule)
同时使用了 CountDownLatch(类似计数器的功能)
(从日志也可以看出,线程的执行并不是按照我们创建或启动的顺序的)
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
public class AbaPro
private static final Random RANDOM = new Random();
private static final String B = "B";
private static final String A = "A";
public static final AtomicReference<String> ATOMIC_REFERENCE = new AtomicReference<>(A);
public static void main(String[] args) throws InterruptedException
final CountDownLatch startLatch = new CountDownLatch(1);
Thread[] threads = new Thread[20];
for (int i=0; i < 20; i++)
threads[i] = new Thread()
@Override
public void run()
String oldValue = ATOMIC_REFERENCE.get();
System.out.println(Thread.currentThread().getName()+ " oldValue: "+ oldValue);
try
startLatch.await();
catch (InterruptedException e)
e.printStackTrace();
System.out.println(Thread.currentThread().getName()+ " 唤醒了 ");
try
Thread.sleep(RANDOM.nextInt()&500);
catch (InterruptedException e)
e.printStackTrace();
System.out.println(Thread.currentThread().getName()+ " 等待一段时间后现在的值: "+ ATOMIC_REFERENCE.get());
// 1. 在这里: 第一次改 & 第三次改 : A -> B
if (ATOMIC_REFERENCE.compareAndSet(oldValue, B ))
System.out.println(Thread.currentThread().getName()+ " 已经对原始值进行了修改,此时值为: "+ ATOMIC_REFERENCE.get());
;
threads[i].start();
System.out.println(Thread.currentThread().getName() +" 即将 count down");
startLatch.countDown();
System.out.println(Thread.currentThread().getName() +" count down done");
Thread.sleep(200);
System.out.println(Thread.currentThread().getName() +" sleep 200ms done");
new Thread()
@Override
public void run()
try
Thread.sleep(RANDOM.nextInt() & 200);
catch (InterruptedException e)
e.printStackTrace();
String oldVal = ATOMIC_REFERENCE.get();
// 2. 在这里: 第二次改 B -> A
while (!ATOMIC_REFERENCE.compareAndSet(ATOMIC_REFERENCE.get(), A));
System.out.println(Thread.currentThread().getName() +" 已经将值 "+oldVal+" 修改成原始值: A");
.start();
日志打印:
Thread-0 oldValue: A
Thread-1 oldValue: A
Thread-2 oldValue: A
Thread-9 oldValue: A
Thread-13 oldValue: A
Thread-6 oldValue: A
Thread-8 oldValue: A
Thread-7 oldValue: A
Thread-12 oldValue: A
Thread-10 oldValue: A
Thread-5 oldValue: A
Thread-4 oldValue: A
Thread-3 oldValue: A
Thread-19 oldValue: A
Thread-15 oldValue: A
Thread-11 oldValue: A
Thread-16 oldValue: A
Thread-18 oldValue: A
Thread-17 oldValue: A
main 即将 count down
main count down done
Thread-2 唤醒了
Thread-14 oldValue: A
Thread-1 唤醒了
Thread-14 唤醒了
Thread-13 唤醒了
Thread-9 唤醒了
Thread-0 唤醒了
Thread-8 唤醒了
Thread-7 唤醒了
Thread-12 唤醒了
Thread-6 唤醒了
Thread-4 唤醒了
Thread-3 唤醒了
Thread-5 唤醒了
Thread-10 唤醒了
Thread-19 唤醒了
Thread-11 唤醒了
Thread-15 唤醒了
Thread-17 唤醒了
Thread-18 唤醒了
Thread-16 唤醒了
Thread-13 等待一段时间后现在的值: A
Thread-13 已经对原始值进行了修改,此时值为: B
Thread-12 等待一段时间后现在的值: B
Thread-8 等待一段时间后现在的值: B
Thread-5 等待一段时间后现在的值: B
Thread-6 等待一段时间后现在的值: B
Thread-7 等待一段时间后现在的值: B
Thread-3 等待一段时间后现在的值: B
Thread-1 等待一段时间后现在的值: B
main sleep 200ms done
Thread-14 等待一段时间后现在的值: B
Thread-18 等待一段时间后现在的值: B
Thread-0 等待一段时间后现在的值: B
Thread-15 等待一段时间后现在的值: B
Thread-4 等待一段时间后现在的值: B
Thread-2 等待一段时间后现在的值: B
Thread-20 已经将值 B 修改成原始值: A
Thread-10 等待一段时间后现在的值: A
Thread-10 已经对原始值进行了修改,此时值为: B
Thread-17 等待一段时间后现在的值: B
Thread-9 等待一段时间后现在的值: B
Thread-16 等待一段时间后现在的值: B
Thread-11 等待一段时间后现在的值: B
Thread-19 等待一段时间后现在的值: B
关键的日志在于:
Thread-13 已经对原始值进行了修改,此时值为: B
Thread-20 已经将值 B 修改成原始值: A
Thread-10 已经对原始值进行了修改,此时值为: B
2. 解决方案
java中提供了AtomicStampedReference来解决这个问题,它是基于版本或者是一种状态,在修改的过程中不仅对比值,也同时会对比版本号
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicStampedReference;
public class AbaProResolve
private static final Random RANDOM = new Random();
private static final String B = "B";
private static final String A = "A";
private static final AtomicStampedReference<String> ATOMIC_STAMPED_REFERENCE = new AtomicStampedReference<>(A, 0);
public static void main(String[] args) throws InterruptedException
final CountDownLatch startLatch = new CountDownLatch(1);
Thread[] threads = new Thread[20];
for (int i = 0; i < 20; i++)
threads[i] = new Thread()
@Override
public void run()
String oldValue = ATOMIC_STAMPED_REFERENCE.getReference();
int stamp = ATOMIC_STAMPED_REFERENCE.getStamp();
try
startLatch.await();
catch (InterruptedException e)
e.printStackTrace();
try
Thread.sleep(RANDOM.nextInt() & 500);
catch (InterruptedException e)
e.printStackTrace();
if (ATOMIC_STAMPED_REFERENCE.compareAndSet(oldValue, B, stamp, stamp + 1))
System.out.println(Thread.currentThread().getName() + " 已经对原始值: " + oldValue + " 进行了修改,此时值为: " + ATOMIC_STAMPED_REFERENCE.getReference());
;
threads[i].start();
Thread.sleep(200);
startLatch.countDown();
new Thread()
@Override
public void run()
try
Thread.sleep(RANDOM.nextInt() & 200);
catch (InterruptedException e)
e.printStackTrace();
int stamp = ATOMIC_STAMPED_REFERENCE.getStamp();
String oldVal = ATOMIC_STAMPED_REFERENCE.getReference();
while (!ATOMIC_STAMPED_REFERENCE.compareAndSet(B, A, stamp, stamp + 1))
stamp = ATOMIC_STAMPED_REFERENCE.getStamp();
System.out.println(Thread.currentThread().getName() + " 已经将值 " + oldVal + " 修改成原始值: A");
.start();
日志打印: A -> B, B -> A
Thread-6 已经对原始值: A 进行了修改,此时值为: B
Thread-20 已经将值 B 修改成原始值: A
参考:
Java并发编程原理与实战四十三:CAS ---- ABA问题 - pony1223 - 博客园 (cnblogs.com)
本文转自 https://www.jianshu.com/p/9a634797ae32,如有侵权,请联系删除。
以上是关于Java- CAS带来的 ABA问题及解决方法的代码实例的主要内容,如果未能解决你的问题,请参考以下文章
Java并发多线程编程——原子类AtomicInteger的ABA问题及原子更新引用
多线程 CAS 机制解析及应用( 原子类 . 自旋锁 )解决 ABA 问题