Atomic & Unsafe

1、Automic

  Java中 Atomic 是指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。 所谓原子类说简单点就是具有原子/原子操作特征的类。Java从JDK1.5开始提供了java.util.concurrent.atomic包,方便程序员在多线程环境下,无锁的进行原子操作。
  在java中可以通过锁和循环CAS的方式来实现原子操作。
JVM中的CAS操作正是利用了上文中提到的处理器提供的CMPXCHG指令实现的。自旋CAS实现的基本思路就是循环进行CAS操作直到成功为止,具体的类可以参见juc下的atomic包内的原子类。
  在Atomic包里一共有12个类,四种原子更新方式,分别是原子更新基本类型,原子更新数组,原子更新引用和原子更新字段。Atomic包里的类基本都是使用Unsafe实现的包装类。

1.2、 原子类分类

  基本类型

    使用原子的方式更新基本类型
  • AtomicInteger:整形原子类
  • AtomicLong:长整型原子类
  • AtomicBoolean:布尔型原子类
  数组类型
    使用原子的方式更新数组里的某个元素
  • AtomicIntegerArray:整形数组原子类
  • AtomicLongArray:长整形数组原子类
  • AtomicReferenceArray:引用类型数组原子类
  引用类型
  • AtomicReference:引用类型原子类

 

  • AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于解决原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题

 

 

  • AtomicMarkableReference :原子更新带有标记位的引用类型

 

  对象的属性修改类型

 

  • AtomicIntegerFieldUpdater:原子更新整形字段的更新器

 

 

  • AtomicLongFieldUpdater:原子更新长整形字段的更新器

 

  • AtomicReferenceFieldUpdater 

1.3 Atomic 的使用

 以 AtomicInteger 为例:

(1)AtomicInteger 的常用方法:

 

public final int get() //获取当前的值
public final int getAndSet(int newValue)//获取当前的值,并设置新的值
public final int getAndIncrement()//获取当前的值,并自增
public final int getAndDecrement() //获取当前的值,并自减
public final int getAndAdd(int delta) //获取当前的值,并加上预期的值
boolean compareAndSet(int expect, int update) //如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update)
public final void lazySet(int newValue)//最终设置为newValue,使用 lazySet 设置之后可能导致其他线程在之后的一小段时间内还是可以读到旧的值。

 

  

 

(2)AtomicInteger 的实现原理

 

//setup to use Unsafe.compareAndSwapInt for updates(更新操作时提供“比较并替换”的作用)
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static
{
    try
    {
        valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
    }
    catch (Exception ex)
    {
        throw new Error(ex);
    }
}
private volatile int value;

 

  AtomicInteger 类主要利用 CAS (compare and swap) + volatile 和 native 方法来保证原子操作,从而避免 synchronized的高开销,执行效率大为提升。CAS的原理是拿期望的值和原本的一个值作比较,如果相同则更新成新的值。UnSafe 类的 objectFieldOffset()方法是一个本地方法,这个方法是用来拿到“原来的值”的内存地址,返回值是valueOffset。另外 value 是一个volatile变量,在内存中可见,因此 JVM 可以保证任何时刻任何线程总能拿到该变量的最新值。

2、Unsafe 类

2.1 介绍

  实际上Atomic包里的类基本都是使用Unsafe实现的包装类。也就是上面的原子类实现过程中都会用到Unsafe类。Java中的Unsafe类提供了类似C++手动管理内存的能力。Unsafe类,全限定名是sun.misc.Unsafe,从名字可以看出来这个类对普通程序员来说是“危险”的,一般应用开发者不会用到这个类。Unsafe类是"final"的,不允许继承。且构造函数是private的,无法在外部对其进行实例化。

public final class Unsafe
{
    private static final Unsafe theUnsafe;
    public static final int INVALID_FIELD_OFFSET = -1;
    private static native void registerNatives();
    // 构造函数是private的,不允许外部实例化
    private Unsafe(){
    }
    @CallerSensitive
    public static Unsafe getUnsafe(){
        Class var0 = Reflection.getCallerClass();
        // 仅在引导类加载器`BootstrapClassLoader`加载时才合法
        if(!VM.isSystemDomainLoader(var0.getClassLoader())){
            throw new SecurityException("Unsafe");
        }
        else{
            return theUnsafe;
        }
    }
}

那如若想使用这个类,该如何获取其实例?有如下两个可行方案。

 2.2 如何使用 Unsafe 类

(1)从getUnsafe方法的使用限制条件出发,通过Java命令行命令-Xbootclasspath/a 把调用Unsafe相关方法的类A所在jar包路径追加到默认的bootstrap路径中,使得A被引导类加载器加载,从而通过Unsafe.getUnsafe方法安全的获取Unsafe实例。

  java -Xbootclasspath/a: ${path} // 其中path为调用Unsafe相关方法的类所在jar包路径。

(2)通过反射来获得Unsafe
public Unsafe getUnsafe() throws IllegalAccessException {
    try{
        Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
        unsafeField.setAccessible(true);
        Unsafe unsafe = (Unsafe) unsafeField.get(null);
        return unsafe;
    }
    catch (Exception e){
        log.error(e.getMessage(), e);
        return null;
    }
}

2.3 Unsafe 的 主要功能

 

 

 

原文链接:https://www.cnblogs.com/liujiarui/p/12811444.html

posted @ 2020-06-17 23:17  风止雨歇  阅读(288)  评论(0编辑  收藏  举报