Java原子操作类型AtomicInteger安全性的验证和使用

什么是原子操作类?

  • 原子操作类是Java在java.util.concurrent包中提供的线程安全的数据类型,本文主要分析AtomicInteger的线程安全性和使用。

为什么我们需要使用原子操作类来进行数据操作?

  • 因为在多线程环境中,同一个变量可能会被多个线程同时修改,这样就无法保证最终的结果是我们所想要得到的结果,而原子操作类可以在不用手动加锁的情况下保证数据的线程安全。

下面用简单的代码来验证原子操作类的线程安全性:

  • 全局上下文,在这里定义我们要操作的数据实例
public class Context {
    public static int addNumTest;//普通的int类型数据
    public static AtomicInteger addNum = new AtomicInteger();//原子integer类型
}
  • 使用int将数据多线程自加3000000次
private static void testAddNumTest(){
    new Thread(new Runnable() {
        @Override
        public void run() {
            for (int i=0;i<1000000;i++){
                System.out.println("1: " + ++Context.addNumTest);
            }
        }
    }).start();

    new Thread(new Runnable() {
        @Override
        public void run() {
            for (int i=0;i<1000000;i++){
                System.out.println("2: " + ++Context.addNumTest);
            }
        }
    }).start();

    new Thread(new Runnable() {
        @Override
        public void run() {
            for (int i=0;i<1000000;i++){
                System.out.println("3: " + ++Context.addNumTest);
            }
        }
    }).start();
}
  • 使用原子操作类将数据多线程自加3000000次
private static void testAddNum(){
    new Thread(new Runnable() {
        @Override
        public void run() {
            for (int i=0;i<1000000;i++){
                System.out.println("1: " + Context.addNum.addAndGet(1));
            }
        }
    }).start();

    new Thread(new Runnable() {
        @Override
        public void run() {
            for (int i=0;i<1000000;i++){
                System.out.println("2: " + Context.addNum.addAndGet(1));
            }
        }
    }).start();

    new Thread(new Runnable() {
        @Override
        public void run() {
            for (int i=0;i<1000000;i++){
                System.out.println("3: " + Context.addNum.addAndGet(1));
            }
        }
    }).start();
}
  • 这里我们分别创建三个线程同时对数据进行自加操作,每个线程中循环1000000次,我们期望得到的结果是3000000,然而我们实际运行会发现,使用int类型的结果每次都会少于3000000,这是因为其中有多个线程同时操作addNumTest的情况,例如A线程读取到的addNumTest为10,与此同时B线程也读取到addNumTest为10,那么它们都会将计算结果11赋值给addNumTest,然而实际上我们希望的是A线程在计算时,B线程应该等到A线程将计算结果赋值给addNumTest后再进行计算,显然使用AtomicInteger的程序则保证了这一点

  • 这里我将循环次数设置得很大,因为发现循环次数少的话很难出现计算结果偏少的情况,个人猜想,这是因为多线程实际是利用获取CPU时间片来实现多个任务处理的,也就是说,对于一个CPU核心而言,只会有一个活动的线程,并不存在真正的线程并发,循环次数很少时,很短的时间即可处理完成,很难用到CPU的多个核心,所以就很少出现计算结果偏少的情况

猜你喜欢

转载自blog.csdn.net/d578332749/article/details/80813206
今日推荐