博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java中的Atomic包
阅读量:6643 次
发布时间:2019-06-25

本文共 4208 字,大约阅读时间需要 14 分钟。

Atomic包的作用

方便程序员在多线程环境下,无锁的进行原子操作

Atomic包核心

Atomic包里的类基本都是使用Unsafe实现的包装类,核心操作是CAS原子操作;

关于CAS

compare and swap,比较和替换技术,将预期值与当前变量的值比较(compare),如果相等则使用新值替换(swap)当前变量,否则不作操作;

现代CPU已广泛支持CAS指令,如果不支持,那么JVM将使用自旋锁,与互斥锁一样,两者都需先获取锁才能访问共享资源,但互斥锁会导致线程进入睡眠,而自旋锁会一直循环等待直到获取锁;

另外,有一点需要注意的是CAS操作中的ABA问题,即将预期值与当前变量的值比较的时候,即使相等也不能保证变量没有被修改过,因为变量可能由A变成B再变回A,解决该问题,可以给变量增加一个版本号,每次修改变量时版本号自增,比较的时候,同时比较变量的值和版本号即可;

Atomic包主要提供四种原子更新方式

  • 原子方式更新基本类型;
  • 原子方式更新数组;
  • 原子方式更新引用;
  • 原子方式更新字段;

原子方式更新基本类型

以下三个类是以原子方式更新基本类型

  • AtomicBoolean:原子更新布尔类型。
  • AtomicInteger:原子更新整型。
  • AtomicLong:原子更新长整型。

以AtomicInteger为例,

package concurrency;import java.util.concurrent.atomic.AtomicInteger;public class AtomicIntegerTest {    static AtomicInteger ai = new AtomicInteger(1);    public static void main(String[] args) {        //相当于i++,返回的是旧值,看方法名就知道,先获取再自增        System.out.println(ai.getAndIncrement());        System.out.println(ai.get());        //先自增,再获取        System.out.println(ai.incrementAndGet());        System.out.println(ai.get());        //增加一个指定值,先add,再get        System.out.println(ai.addAndGet(5));        System.out.println(ai.get());        //增加一个指定值,先get,再set        System.out.println(ai.getAndSet(5));        System.out.println(ai.get());    }}

注意:Atomic包提供了三种基本类型的原子更新,剩余的Java的基本类型还有char,float和double等,其更新方式可以参考AtomicBoolean的思路来现,AtomicBoolean是把boolean转成整型再调用compareAndSwapInt进行CAS来实现的,类似的short和byte也可以转成整形,float和double可以利用Float.floatToIntBits,Double.doubleToLongBits转成整形和长整形进行相应处理;

原子方式更新数组

以下三个类是以原子方式更新数组,

  • AtomicIntegerArray:原子更新整型数组里的元素。
  • AtomicLongArray:原子更新长整型数组里的元素。
  • AtomicReferenceArray:原子更新引用类型数组里的元素。

以AtomicIntegerArray为例,其方法与AtomicInteger很像,多了个数组下标索引;

package concurrency;import java.util.concurrent.atomic.AtomicIntegerArray;public class AtomicIntegerArrayTest {    static int[] valueArr = new int[] { 1, 2 };    //AtomicIntegerArray内部会拷贝一份数组    static AtomicIntegerArray ai = new AtomicIntegerArray(valueArr);    public static void main(String[] args) {        ai.getAndSet(0, 3);        //不会修改原始数组value        System.out.println(ai.get(0));        System.out.println(valueArr[0]);    }}

原子方式更新引用

以下三个类是以原子方式更新引用,与其它不同的是,更新引用可以更新多个变量,而不是一个变量;

  • AtomicReference:原子更新引用类型。
  • AtomicReferenceFieldUpdater:原子更新引用类型里的字段。
  • AtomicMarkableReference:原子更新带有标记位的引用类型。

以AtomicReference为例,

package concurrency;import java.util.concurrent.atomic.AtomicReference;public class AtomicReferenceTest {    public static AtomicReference
atomicUserRef = new AtomicReference
(); public static void main(String[] args) { User user = new User("conan", 15); atomicUserRef.set(user); User updateUser = new User("Shinichi", 17); atomicUserRef.compareAndSet(user, updateUser); System.out.println(atomicUserRef.get().getName()); System.out.println(atomicUserRef.get().getOld()); } static class User { private String name; private int old; public User(String name, int old) { this.name = name; this.old = old; } public String getName() { return name; } public int getOld() { return old; } }}

原子方式更新字段

以下三个类是以原子方式更新字段,

  • AtomicIntegerFieldUpdater:原子更新整型字段的更新器。
  • AtomicLongFieldUpdater:原子更新长整型字段的更新器。
  • AtomicStampedReference:原子更新带有版本号的引用类型,用于解决使用CAS进行原子更新时,可能出现的ABA问题。

以AtomicIntegerFieldUpdater为例,

package concurrency;import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;public class AtomicIntegerFieldUpdaterTest {    private static AtomicIntegerFieldUpdater
a = AtomicIntegerFieldUpdater .newUpdater(User.class, "old"); public static void main(String[] args) { User conan = new User("conan", 10); System.out.println(a.getAndIncrement(conan)); System.out.println(a.get(conan)); } public static class User { private String name; //注意需要用volatile修饰 public volatile int old; public User(String name, int old) { this.name = name; this.old = old; } public String getName() { return name; } public int getOld() { return old; } }}
本文转自风一样的码农博客园博客,原文链接:http://www.cnblogs.com/chenpi/p/5375805.html,如需转载请自行联系原作者
你可能感兴趣的文章
vim实用配置(转)
查看>>
实现MAXIMO7.5工作流任务箱任务颜色提示功能
查看>>
SpringMVC 集成redis
查看>>
Solaris作业管理
查看>>
回顾2016,我的简单总结
查看>>
3372 选学霸
查看>>
ssh: connect to host localhost port 22: Connection refused 问题
查看>>
Adobe Photoshop CS或者CC卸载不了怎么办?
查看>>
怎样重置网络设置-出现打不开网站的时候可以用用
查看>>
【转】30岁之前打好基础,无惧职场“35岁现象”! | 人力资源心理学
查看>>
分布式搜索引擎Elasticsearch PHP类封装 使用原生api
查看>>
asp.net AJAX 定期刷新页面,然后,在 Timer 的事件中弹出窗口
查看>>
potrace源码分析一
查看>>
using eclipse to write c programe 0
查看>>
记录一下收集到的clojure相关的东东
查看>>
《Linux内核原理与分析》第七周作业
查看>>
浅析Xilinx 三速以太网MAC IP核(仿真篇)
查看>>
(转)Fidder教程
查看>>
UNREFERENCE_PARAMETER
查看>>
Linux -RAID
查看>>