多线程编程(六)原子类

原子类

1. 原子类分类

原子类基本都在java.util.concurrent.atomic 包中

1.1 原子基本类型

  • AtomicBoolean
  • AtomicInteger
  • AtomicLong

1.2 原子数组类型

  • AtomicIntegerArray
  • AtomicLongArray

1.3 原子引用类型

  • AtomicReference
  • AtomicStampedReference

1.4 原子属性类型

  • AtomicLongFieldUpdater
  • AtomicIntegerFieldUpdater

2. 非原子类实例

测试代码

public class Test {

    static Long totalCount = 0L;

    public static void main(String[] args) {

        int j = 0;
        while (j < 100) {
            totalCount = 0L;

            Thread thread1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 500; i++) {
                        totalCount++;
                    }
                }
            }, "线程1");


            Thread thread2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 500; i++) {
                        totalCount++;
                    }
                }
            }, "线程1");


            thread1.start();
            thread2.start();
            try {
                thread1.join();
                thread2.join();
                System.out.println("当前总数量为:" + totalCount);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            j++;
        }
    }
}

测试结果

当前总数量为:884
当前总数量为:717
当前总数量为:563
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:686
当前总数量为:1000
...

结论 :可以看出非原子类在多线程的时候,获得的结果并不是我们想要的

3. 原子类实例

测试代码

public class Test {

   // static Long totalCount = 0L;

   static AtomicInteger totalCount;

    public static void main(String[] args) {

        int j = 0;
        while (j < 100) {
            //原子类,初始值为0
            totalCount =  new AtomicInteger(0);

            Thread thread1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 500; i++) {
                        //totalCount++;
                        totalCount.getAndIncrement();
                    }
                }
            }, "线程1");


            Thread thread2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 500; i++) {
                        //totalCount++;
                        totalCount.getAndIncrement();
                    }
                }
            }, "线程1");


            thread1.start();
            thread2.start();
            try {
                thread1.join();
                thread2.join();
               // System.out.println("当前总数量为:" + totalCount);
                System.out.println("当前总数量为:" + totalCount.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            j++;
        }
    }
}

测试结果

当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000

结论 : AtomicInteger 可以保证在多线程的情况下数据的原子性

4. AtomicInteger 常用方法

  • getAndIncrement 先使用再自增1,相当于 n++
  • getAndDecrement 先使用,再自减1,相当于 n–
  • decrementAndGet 先减1,再使用,相当于–n
  • incrementAndGet 先加1,在使用。相当于 ++n
  • get 获取当前的值

4. AtomicInteger 源码分析

使用的是unsafe 方法的getAndAddInt 来进行自增的
this: 为当前的值
valueOffset 地址偏移量,助你找到n在堆中的地址

public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

getIntVolatile 通过volatile直接获取内存中获取到预期值 :var5
var1:当前值
var2 :地址偏移量
var5:预期值

compareAndSwapInt 进行CAS操作
var1:当前值
var2 :地址偏移量
var4 :递增间隔
var5:预期值
比较 var1 和 var5 是否相同,
相同,代表没有线程更改过,,当前值(var1) = 预期值(var5) + 递增间隔 (var4) 返回true
不相同,代表线程更改过,,当前值(var1) = 预期值(var5) 返回false,再重试

public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

5. ABA问题

引发原因

  1. 线程1 将var1 =A 读取到线程内存中(栈),
  2. 线程2 将var1 改成B ,又改成成A
  3. 线程1 在获取预期值var5时, 发现还是A, 符合预期,导致没有返现被更改
  4. 其实线程2已经做过更改

6. ABA解决

解决思路,对变量增加时间戳或版本号,这样的话,改了的话,时间戳或版本号会更改,在CAS的时候能被发现

AtomicStampedReference 解决ABA问题
getReference 获取预期值
getStamp 获取时间戳

测试代码

public class ABATest {

    private static AtomicStampedReference<Integer> totalCount;

    public static void main(String[] args) {

        int j = 0;
        while (j < 100) {
            //原子类,初始值为0
            totalCount = new AtomicStampedReference<>(0, 0);

            Thread thread1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 500; i++) {
                        //totalCount++;
                        Integer reference;
                        int stamp;
                        do {
                            reference = totalCount.getReference();
                            stamp = totalCount.getStamp();
                        } while (!totalCount.compareAndSet(reference, reference + 1, stamp, stamp + 1));
                    }
                }
            }, "线程1");


            Thread thread2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 500; i++) {
                        Integer reference;
                        int stamp;
                        do {
                            reference = totalCount.getReference();
                            stamp = totalCount.getStamp();
                        } while (!totalCount.compareAndSet(reference, reference + 1, stamp, stamp + 1));

                    }
                }
            }, "线程1");


            thread1.start();
            thread2.start();
            try {
                thread1.join();
                thread2.join();
                // System.out.println("当前总数量为:" + totalCount);
                System.out.println("当前总数量为:" + totalCount.getReference());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            j++;
        }
    }
}


测试结果

当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
当前总数量为:1000
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值