Atomic的介绍和使用(原子变量)

开始之前,我们来看一下上一篇文章中《CAS (全 ) && concurrent包的实现》中提到了concurrent包的实现图。
下图中的原子变量类就是Atomic类中的一部分。
也就是说,atomic类首先是一个乐观锁,然后底层实现也是根据CAS操作和Volatile关键字实现的。
在这里插入图片描述

什么是线程安全?
**线程安全是指多线程访问是时,无论线程的调度策略是什么,程序能够正确的执行。**导致线程不安全的一个原因是状态不一致,如果线程A修改了某个共享变量(比如给id++),而线程B没有及时知道,就会导致B在错误的状态上执行,结果的正确性也就无法保证。原子变量为我们提供了一种保证单个状态一致的简单方式,一个线程修改了原子变量,另外的线程立即就能看到,这比通过锁实现的方式效率要高;如果要同时保证多个变量状态一致,就只能使用锁了。

Atomic
在JDK1.5之后,JDK的(concurrent包)并发包里提供了一些类来支持原子操作,如AtomicBoolean,AtomicInteger,AtomicLong都是用原子的方式来更新指定类型的值。

从多线程并行计算乐观锁 和 悲观锁 来讲,JAVA中的synchronized 属于悲观锁,即是在操作某数据的时候总是会认为多线程之间会相互干扰,属于阻塞式的加锁;Atomic系列则属于乐观锁系列,即当操作某一段数据的时候,线程之间是不会相互影响,采用非阻塞的模式,直到更新数据的时候才会进行版本的判断是否值已经进行了修改。

sun.misc.Unsafe
这个类包含了大量的对C代码的操作,包括了很多直接内存分配和原子操作的调用,都存在安全隐患,所以标记为unsafe。
AtomicInteger是一个标准的乐观锁实现,sun.misc.Unsafe是JDK提供的乐观锁的支持。

Atomic在JAVA中的家族如下:
a、基本类:AtomicInteger、AtomicLong、AtomicBoolean;
b、引用类型:AtomicReference、AtomicReference的ABA实例、AtomicStampedRerence、AtomicMarkableReference;
c、数组类型:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
d、属性原子修改器(Updater):AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater

Atomic的使用测试和讲解 见:https://blog.csdn.net/xh16319/article/details/17056767
这里我们不对每一个Atomic类进行详细的测试和讲解,我们只需要知道Atomic底层是通过CAS操作实现的数据的原子性,其中AtomicStampedReference类解决了ABA问题(ABA问题在《CAS (全 ) && concurrent包的实现》中也有讲解)

我们在这里只对 AtomicStampedReference 类和AtomicMarkableReference类的使用进行简单描述,读者理解其用法即可。

import java.util.concurrent.atomic.AtomicStampedReference;  
  
public class AtomicStampedReferenceTest {     
    public final static AtomicStampedReference <String>ATOMIC_REFERENCE = new AtomicStampedReference<String>("abc" , 0);  
      
    public static void main(String []args) {  
        for(int i = 0 ; i < 100 ; i++) {  
            final int num = i;  
            final int stamp = ATOMIC_REFERENCE.getStamp();  
            new Thread() {  
                public void run() {  
                    try {  
                        Thread.sleep(Math.abs((int)(Math.random() * 100)));  
                    } catch (InterruptedException e) {  
                        e.printStackTrace();  
                    }  
                    if(ATOMIC_REFERENCE.compareAndSet("abc" , "abc2" , stamp , stamp + 1)) {  
                        System.out.println("我是线程:" + num + ",我获得了锁进行了对象修改!");  
                    }  
                }  
            }.start();  
        }  
        //下面这个线程是为了将数据改回原始值,以便之后的操作
        new Thread() {  
            public void run() {  
                int stamp = ATOMIC_REFERENCE.getStamp();  
                while(!ATOMIC_REFERENCE.compareAndSet("abc2", "abc" , stamp , stamp + 1));  
                System.out.println("已经改回为原始值!");  
            }  
        }.start();  
    }  
}  

其中的compareAndSet函数的源码中就用到了之前文章中讲解的CAS操作:

public final boolean compareAndSet(V expect, V update) {  
    return unsafe.compareAndSwapObject(this, valueOffset, expect, update);  
}  

类:AtomicMarkableReference和AtomicStampedReference功能差不多,有点区别的是:它描述更加简单的是与否的关系,通常ABA问题只有两种状态,而AtomicStampedReference是多种状态,那么为什么还要有AtomicMarkableReference呢,因为它在处理是与否上面更加具有可读性,而AtomicStampedReference过于随意定义状态,并不便于阅读大量的是和否的关系,它可以被认为是一个计数器或状态列表等信息,java提倡通过类名知道其意义,所以这个类的存在也是必要的,它的定义就是将数据变换为true|false如下:

public final static AtomicMarkableReference <String>ATOMIC_MARKABLE_REFERENCE = new AtomicMarkableReference<String>("abc" , false);  

操作时使用:

ATOMIC_MARKABLE_REFERENCE.compareAndSet("abc", "abc2", false, true);  

另外我们来简单介绍一些Atomic中的扩展类:
java提供一个外部的Updater可以对对象的属性本身的修改提供类似Atomic的操作,也就是它对这些普通的属性的操作是并发下安全的,分别由:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceUpdater

这样操作后,系统会更加灵活,也就是可能那些类的属性只是在某些情况下需要控制并发,很多时候不需要,但是他们的使用通常有以下几个限制:
限制1:操作的目标不能是static类型,前面说到unsafe的已经可以猜测到它提取的是非static类型的属性偏移量,如果是static类型在获取时如果没有使用对应的方法是会报错的,而这个Updater并没有使用对应的方法。

限制2:操作的目标不能是final类型的,因为final根本没法修改。

限制3:必须是volatile类型的数据,也就是数据本身是读一致的。

限制4:属性必须对当前的Updater所在的区域是可见的,也就是private如果不是当前类肯定是不可见的,protected如果不存在父子关系也是不可见的,default如果不是在同一个package下也是不可见的。

**实现方式:**通过反射找到属性,对属性进行操作,但是并不是设置accessable,所以必须是可见的属性才能操作。

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;  
  
public class AtomicIntegerFieldUpdaterTest {  
  
    static class A {  
        volatile int intValue = 100;  
    }  
      
    /** 
     * 可以直接访问对应的变量,进行修改和处理 
     * 条件:要在可访问的区域内,如果是private或挎包访问default类型以及非父亲类的protected均无法访问到 
     * 其次访问对象不能是static类型的变量(因为在计算属性的偏移量的时候无法计算),也不能是final类型的变量(因为根本无法修改),必须是普通的成员变量 
     *  
     * 方法(说明上和AtomicInteger几乎一致,唯一的区别是第一个参数需要传入对象的引用) 
     * @see AtomicIntegerFieldUpdater#addAndGet(Object, int) 
     * @see AtomicIntegerFieldUpdater#compareAndSet(Object, int, int) 
     * @see AtomicIntegerFieldUpdater#decrementAndGet(Object) 
     * @see AtomicIntegerFieldUpdater#incrementAndGet(Object) 
     *  
     * @see AtomicIntegerFieldUpdater#getAndAdd(Object, int) 
     * @see AtomicIntegerFieldUpdater#getAndDecrement(Object) 
     * @see AtomicIntegerFieldUpdater#getAndIncrement(Object) 
     * @see AtomicIntegerFieldUpdater#getAndSet(Object, int) 
     */  
    public final static AtomicIntegerFieldUpdater <A>ATOMIC_INTEGER_UPDATER = AtomicIntegerFieldUpdater.newUpdater(A.class, "intValue");  
      
    public static void main(String []args) {  
        final A a = new A();  
        for(int i = 0 ; i < 100 ; i++) {  
            final int num = i;  
            new Thread() {  
                public void run() {  
                    if(ATOMIC_INTEGER_UPDATER.compareAndSet(a, 100, 120)) {  
                        System.out.println("我是线程:" + num + " 我对对应的值做了修改!");  
                    }  
                }  
            }.start();  
        }  
    }  
}  

猜你喜欢

转载自blog.csdn.net/mulinsen77/article/details/84579858