原子变量与CAS算法

时间:2023-03-08 20:36:09

原子变量

为了引出原子变量这个概念,我们先看一个例子。

 package com.ccfdod.juc;

 public class TestAtomicDemo {

     public static void main(String[] args) {
AtomicDemo ad = new AtomicDemo(); for (int i = 0; i < 10; i++) {
new Thread(ad).start();
}
}
} class AtomicDemo implements Runnable {
private int number = 0; @Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " : " + getNumber());
} public int getNumber() {
return number++;
}
}

程序运行结果如下:

 Thread-4 : 2
Thread-0 : 6
Thread-2 : 3
Thread-5 : 5
Thread-7 : 4
Thread-3 : 1
Thread-6 : 2
Thread-1 : 0
Thread-9 : 8
Thread-8 : 7

从程序运行结果可以看出,Thread-4和Thread-6执行结果都为2,明显发生了线程安全问题,当然,这种情况是偶然的。那么,出现这种问题的原因是什么呢?

如果你对j = i++;底层是如果实现的,那么这个问题就好理解了。j = i++;底层实现为:

int temp = i;
i = i + 1;
j = temp;

那么很明显,Thread-4(或Thread-6)在执行改操作加1之前,Thread-6(或Thead-4)读到了相同的值。然后都进行加1操作,再打印出来。

对于这类问题,我们可以使用原子变量来解决。在jdk1.5后,java.util.concurrent.atomic包中提供了常用的原子变量。原子变量有一下特性:

  • 底层代码使用volatile修饰,保证内存可见性
  • CAS(Compare-And-Swap)算法保证数据的原子性

CAS算法

CAS算法是硬件对于并发操作共享数据的支持,CAS包含了三个操作数:

  • 内存值V
  • 预估值A
  • 更新值B

并且,当且仅当V==A时,V=B,否则,将不做任何操作。

在了解了原子变量后,我们使用原子变量修改程序:

 class AtomicDemo implements Runnable {
// private int number = 0;
private AtomicInteger number = new AtomicInteger(); @Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " : " + getNumber());
} public int getNumber() {
// return number++;
return number.getAndIncrement();
}
}