atomic包的简单总结

1 篇文章 0 订阅
1 篇文章 0 订阅

我们知道synchronized 关键字采用的是悲观锁的方式实现同步,但这对并发性的影响较大。atomic包提供了一系列的操作简单、性能高效并能保证线程安全的类去更新基本类型变量、数组元素、引用类型等。atomic包下的这些类都是采用的是乐观锁策略去原子更新数据,使用CAS操作具体实现。
1.CAS操作
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。因此当多个线程同时尝试使用CAS更新一个变量时,任何时候只有一个线程可以更新成功,若更新失败,线程会重新进入循环再次进行尝试。
如果对上面这个描述不能理解的话,看一下这个例子。比如线程A和线程B同时在对int型的变量 i 进行自增操作。假如 i=0,此时线程A和线程B同时启动,它们极有可能返回的结果都是1。仔细分析一下运行过程:线程A启动,并将变量 i=0 拷贝到工作内存A;此时线程B也启动,并且将变量i=0 拷贝到工作内存B;当A完成操作后将 i=1 写回主内存,线程B也做同样的操作。此时的结果当然都是1,因为线程B根本就不知道线程A已经更新了主内存的值啊。这就是一个典型的线程安全问题,我们当然可以用互斥锁来实现线程安全,只需要让线程A访问i的时候拒绝线程B的访问,但是也可以用乐观锁来实现,例如CAS操作。当线程A将自己工作内存里的i(此时为1)更新到主内存时,做一次CAS操作,A预期主内存里的i应该是0,而事实就是如此,因此A将新值1更新给i。接下来让线程B尝试写回主内存,B预期的i也应该是0(因为B看到的i的旧值就是0),结果看到的i实际值是1,那么线程B会拿走这个值1(因为B意识到了这段时间内i被更改了),进行自增操作后再次试图更新i(此时B的工作内存里i=2),这次发现主内存里i确实是预期的1,于是更新主内存里的i为2,这就是正确的逻辑了。
总的来说,内存值V指的是线程在工作内存里更新完了之后试图将值更新到主内存的时候主内存里的值,旧的预期值A指线程从主内存取值时的值,新值B指的是工作内存里更新完了之后试图将值更新到主内存的时候工作内存里的值。
由于这里主要讲CAS操作主要是为了总结Atomic包,对CAS引发的ABA问题不做说明。
2.atomic包提供原子更新基本类型的工具类

  1. AtomicBoolean:以原子更新的方式更新boolean;
  2. AtomicInteger:以原子更新的方式更新Integer;
  3. AtomicLong:以原子更新的方式更新Long;

这几个类的用法基本一致,这里以AtomicInteger为例总结常用的方法:

  1. addAndGet(int delta) :以原子方式将输入的数值与实例中原本的值相加,并返回最后的结果;
  2. incrementAndGet() :以原子的方式将实例中的原值进行加1操作,并返回最终相加后的结果;
  3. getAndSet(int newValue):将实例中的值更新为新值,并返回旧值;
  4. getAndIncrement():以原子的方式将实例中的原值加1,返回的是自增前的旧值;

还有一些方法,可以查看API,不再赘述。想知道方法原理也可以直接查API,其实很多方法看方法名就知道运行结果了,下面用一个简单的例子来说明AtomicInteger的用法:

public class AtomicDemo {
    private static AtomicInteger atomicInteger = new AtomicInteger(1);

    public static void main(String[] args) {
        System.out.println(atomicInteger.getAndIncrement());
        System.out.println(atomicInteger.get());
        System.out.println(atomicInteger.addAndGet(2));
        System.out.println(atomicInteger.get());
        System.out.println(atomicInteger.incrementAndGet());
        System.out.println(atomicInteger.get());
        System.out.println(atomicInteger.getAndSet(2));
        System.out.println(atomicInteger.get());
    }
}
输出结果:
1
2
4
4
5
5
5
2

3.atomic包提供原子更新数组的工具类

  1. AtomicIntegerArray:原子更新整型数组中的元素;
  2. AtomicLongArray:原子更新长整型数组中的元素;
  3. AtomicReferenceArray:原子更新引用类型数组中的元素

这几个类的用法一致,就以AtomicIntegerArray来总结下常用的方法:

  1. addAndGet(int i, int delta):以原子更新的方式将数组中索引为i的元素与输入值相加;
  2. getAndIncrement(int i):以原子更新的方式将数组中索引为i的元素自增加1;
  3. compareAndSet(int i, int expect, int update):将数组中索引为i的位置的元素进行更新

可以看出,AtomicIntegerArray与AtomicInteger的方法类似,只不过在AtomicIntegerArray的方法中会多一个指定数组索引位。下面举一个简单的使用getAndAdd()方法的例子:

public class AtomicDemo {
    private static int[] value = new int[]{1, 2, 3};
    private static AtomicIntegerArray integerArray = new AtomicIntegerArray(value);

    public static void main(String[] args) {
        int result = integerArray.getAndAdd(1, 5);
        System.out.println(integerArray.get(1));
        System.out.println(result);
    }
}
输出结果:
7
2

4.原子更新引用类型
如果需要原子更新引用类型变量的话,为了保证线程安全,atomic也提供了相关的类:

  1. AtomicReference:原子更新引用类型;
  2. AtomicReferenceFieldUpdater:原子更新引用类型里的字段;
  3. AtomicMarkableReference:原子更新带有标记位的引用类型;

这几个类的使用方法也是基本一样的,以AtomicReference为例,来说明这些类的基本用法。下面是一个示例:

public class AtomicDemo {

    private static AtomicReference<User> reference = new AtomicReference<>();

    public static void main(String[] args) {
        User user1 = new User("a", 1);
        reference.set(user1);
        User user2 = new User("b",2);
        User user = reference.getAndSet(user2);
        System.out.println(user);
        System.out.println(reference.get());
    }

    static class User {
        private String userName;
        private int age;

        public User(String userName, int age) {
            this.userName = userName;
            this.age = age;
        }

        @Override
        public String toString() {
            return "User{" +
                    "userName='" + userName + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
}

输出结果:
User{userName='a', age=1}
User{userName='b', age=2}

注意此处reference.getAndSet(user2)方法是先返回reference指向的实例,再将reference指向user2。

总的来说,atomic包提供了一些类,它们以原子操作的方式更新变量,实现原理就是CAS操作,在这篇博客里我就不对原理加以展示了,官方文档有很详细的说明。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值