原子操作类
在Java并发编程中,由于多线程安全的原因,我们在对共享变量进行操作访问时,会经常得到不正确的结果,比如说m = 1的共享变量,在两个线程并发地执行m += 1;之后,理应是m = 3,然后得到的结果可能是2,因为拿到的结果可能是某个线程操作后还未同步的值。从1.5开始,JDK提供了atomic包,里面包括了4种类型的原子更新方式,分别是原子更新基本类型、原子更新数组、原子更新引用和原子更新属性。纵然我们可以使用锁同步来解决共享变量访问安全的问题,但使用原子操作类将会更方便。
原子操作基本类有AtomicBoolean、AtomicInteger、AtomicLong三个,分别对应基本数据类型的boolean、int、long三种类型。这三个原子操作类提供了一组原子操作,addAndGet(int delta)方法是在原数据基本上加上一个值并返回结果,compareAndSet(int expect,int update)是比较当前值的输入值,如果相等就设置值,getAndIncrement()是返回值并将值自增1,lazySet(int newValue)是设置值,但在设置值后的一小段时间里,其他线程可能会访问到设置之前的值,getAndSet(int
newValue)是返回值并且设置一个新值。这几个方法都是原子操作,也就是说在并发环境下不会引起线程安全问题。这些原子操作类都是基于Unsafe实现的,在Unsafe中提供了三种CAS方法。
原子更新数组提供了3个类,分别是AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray。和基本类型原子操作类一样,提供了一些原子操作方法,例如addAndGet(int i,int data)方法是将下标为i的数组元素加上data并作为结果返回,其他的方法类似,都指明了下标,然后原子操作指定下标的数组元素。这里要注意的一点是构造原子操作数组对象的时候,原子操作数组是把传入的数组复制了一份,而不存传入数组的引用,因此对原子操作数组进行修改元素值时不会对传入的数组产生影响。
原子更新引用类型提供了AtomicReference、AtomicReferenceFieldUpdater和AtomicMarkableReference三个类。这三个类可以引用对象变量,然后就可以对变量进行原子操作了。
原子更新字段类可以把类的某个字段包装成原子,这样在操作其他字段时可以非原子操作,具体更大的操作性,atomic提供的方法和前面的原子操作类类似。
很多时候,我们知道哪些变量会产生线程安全问题,而有些变量不会,为了方便,我们可以把会产生线程安全问题的变量包装成原子变量,这样就可以像操作普通变量一样来操作可能会有线程安全问题的变量了而不必使用线程锁,而且使用原子操作类更加轻量级,不管是在代码级别还是低层数据级别。