在多线程环境下,我们要实现对一个变量自增的话,往往会使用java.util.concurrent.atomic包下的相关实现类。 如下:
public class TestAtomic {
public static void main(String[] args) {
ThreadDemo td=new ThreadDemo();
//开启二十个线程完成自加操作
for(int x=1;x<=20;x++){
new Thread(td).start();
}
}
}
class ThreadDemo implements Runnable {
//定义一个AtomicInteger的初始值为0的变量atoInt
private AtomicInteger atoInt=new AtomicInteger(0);
@Override
public void run() {
try {
//休眠一下,提高并发性
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getNum());
}
public int getNum() {
//每次返回的为当前值的下一个值
return atoInt.incrementAndGet();
}
}
这里用到原子并发包装类
1:对于变量,原子性操作都用到了volatile关键字,确保线程之间的可见性。
2:用到了CAS(Compare and Swap)算法
CAS算法
这里用到的主要有三个值
主存值 V :从主存中取出值
预估值 A :对于非原子性的操作,再进行下一次计算操作之前,会再次读取主存中的值
更新值 B :根据具体的逻辑,有待跟新的值
这里有关主存的问题,可以参考 这篇文章,在CAS算法中,如果 V==A 则把B的值复制给V,否则什么也不做,并进行下一次尝试。
* 比如 对于
* int num=0;
* i=num++;这个操作
* 在进行num++这个操作的时候,先从主存中获取num的值,然后再进++操作
* 很明显这是两个步骤,在获取num的值之后,另一个线程可能已经更改了num的值,这时候加的时候明显有问题
* CAS算法如下:
* 获取主存值 V=0
* 执行之后,在查看一个主存的值 此时如果还是 A=0(没有线程修改)
* 根据num++的操作,此时B应该更新到值为0+1=1
* V=0
* A=0
* B=1
* 因为V==A 此时把B的值刷新到主存中V=1;
* ************************************************
* 如果在一个线程读取之后,另一个线程修改了值,即A=1
* V=0
* A=1
* B=1
* V!=A,则不会把B的值刷新到主存中
*/