19-JDK5提供的原子类的操作以及实现原理

本节继续了解关于线程安全性问题的一个解决方案,我们之前对于之前所出现的线程安全性问题已经了解了两个解决方案,第一个是synchronized,第二个是volatile。但是对于我们之前的数值序列生成器来讲,使用volatile并不是一个合理的解决方案,因为volatile只能够保证读和写的多个线程之间的可见性,但是,并不能够保证原子性,那么,++(自增)并不是一个原子性操作,因此,volatile解决不了这个问题,那么,我们本节再来学习另外一种解决线程安全性问题的方法,就是,原子类,原子类是保证就是提供原子性操作,那么,这个是在JDK5的时候才出现的,我们来看一下它如何使用,本节就对原子类的原理以及使用进行详细的讲解。

JDK提供了很多的原子类,而且也有非常多的类型,那么,我们就来分别来看一下原子类,它的核心都在rt.jar中,

 

 

 

这里面就是所提供的原子类,其实我们可以大致的把它们分成四类,

 

 

我们也就从这四类里面去进行讲解。

DoubleAdder是在JDK8的时候才出现的。Doug Lea并不满足于Atomic所提供的性能问题,因此,在JDK8的时候,又增加了关于性能更好的这么一个LongAdder这样一个工具类,到后面我们也专门涉及了这么一块内容来给大家进行讲解这个问题,LongAdder能够提供更好的并发的支持,提供更好的性能。我们本节课就来看一下这么几个Atomic原子类。

首先我们先来拿Atomic来解决我们之前所遇到的数值序列生成器的安全性问题,把之前的数值序列生成器的代码直接copy过来,在这个代码里面我们对这个代码稍加修改,不使用synchronized了,我们始终再说,synchronized由于上下文切换浪费时间浪费资源,性能不好。下面我们就用原子类来进行解决这个线程安全性问题。

这里不是一个原子性操作,而提供的原子类就可以把这个操作作为一个原子性操作,怎么做呢?

我们就不再用int了,而是使用AtomicInteger这个类

AtomicInteger就是一个原子性的Integer类,它就可以对int类型进行原子性操作,我们把AtomicIntger对象创建出来就可以了,而且AtomicInteger的构造方法里面我们可以传入一个初始值,本例中初始值传入为0

那么,此时在进行++操作的时候就不能够在进行直接++了,

而是应该通过AtomicInteger对象调用AtomicInteger的方法,

这就是自增,获取并且增加,就是所谓的++,即value++,那么,++value该怎么写呢?先自增再获取,

这就是++在前。++在前和++在后对应的原子性操作也是不同的。我们先不管它的原理,先来看执行的结果,

线程安全了。

这就是关于使用AtomicInteger原子类来进行操作。如果我们有对基本类型进行原子性操作,那么,就可以使用这个方法,那么,比如我们这里已经看到了,有getAndIncrement()等,--就是getAndDecrement(),--在前就是decrementAndGet()

那么,还有,比如说我们想每次加10,

我们对数值的那种非原子性操作,在这里面都可以转化成原子性操作,一旦转化成原子性操作之后,也就不存在线程安全性问题了。除了AtomicInteger以外,

发现里面没有对char类型的操作,其实对于char类型的操作,char类型里面所保存的也就是字符的编码,那么我们就可以同样的去把它转成int类型来进行操作,其实这里面跟Java虚拟机的字节码指令的数据类型上有一定的相似度,我们说很多的,比如说char类型,包括其他的一些类型,比如说iadd这个字节码指令,也就有这么几个,那么,其他的可能很多的都会使用iadd这个来进行整数的相加,它这个设计的原理其实是一样的,这就是使用原子类来进行解决线程安全性问题。那么,除了我们刚才说的使用原子更新基本类型以外,还有

我们分别来看一下,我们就不在进行对线程的演示了,仅仅是来看一下它的API,看完API之后再来看它的原理,

首先是更新数组,那么,首先我们要创建一个数组,本例,创建一个Integer类型的数组,

你可以给它一个初始化数组,也可以只给他一个数组的长度,也就是说,这个数组,即使是AtomicIntegerArray,它也是定长的。比如说我们就把我们创建的Integer 类型的数组s传给它就可以了

发现报错了,是类型不对,我们把s换成int类型的数组

原子更新抽象类型,也就是所谓的引用类型。

我们这里需要定义一个抽象类型,比如我们这里定义一个User类,

 

原子操作仅仅是对,大家一定要想明白了啊,是对User这个类的实例进行get和set,而不是对User中的属性信息进行保证原子性操作,说到这里想必大家也都明白了,也就是,无非就是对User的get和set。

最后一个是原子更新字段类,字段类就是类的字段,这个用的是

这个就是更新一个类的Integer类型的字段,泛型类这里面所接收的应该是一个类的实例,比如说我们要更新User类的old属性,

它是一个抽象类,那么,这个抽象类里面就得提供我们获取它实例的方法,

你要更新哪个字段?

这里是一个String类型的,我们要更新的就是User类的old字段,

这样就可以了,那么,下面我们来更新它,

报错了

 

 

我们把它设置为public

依然报错

 

old必须要声明为volatile,

再次运行

发现已经执行了。

我们需要更新的字段必须得使用volatile进行修饰。这就是关于更新字段。

下面我们来看它实现的原理,我们就来看一个比较简单的,就是getAndIncrement(),

它其实底层所使用到的都是unsafe类,

unsafe类是不对外开放的源代码的,所以,我们这里就先暂时不研究它。所以,这里我们也就没有办法看它的源码了。

 

猜你喜欢

转载自blog.csdn.net/G_66_hero/article/details/86221337
今日推荐