Java并发编程之CAS和Unsafe类本地使用方法-创新互联

Java 并发编程之CAS 和 Unsafe类本地使用方法

成都创新互联公司是一家企业级云计算解决方案提供商,超15年IDC数据中心运营经验。主营GPU显卡服务器,站群服务器,成都联通服务器托管,海外高防服务器,大带宽服务器,动态拨号VPS,海外云手机,海外云服务器,海外服务器租用托管等。文章目录
  • Java 并发编程之CAS 和 Unsafe类本地使用方法
    • CAS原理与Unsafe类
    • Unsafe类本地使用方法

CAS原理与Unsafe类

我们知道保证线程安全的三个要素是原子性,可见性,有序性

CAS(Compare And Swap),指令级别保证某一内存地址V上的值的更新修改是一个原子操作
需要三个值:

  • 内存地址V

  • 该线程拿到的值A

  • 期望更新后的值B

思路:如果地址V上的实际值和该线程拿到的值A相等,就给地址V赋给新值B,如果不是,不做任何操作。
循环(死循环,自旋)里不断的进行CAS操作

JDK里为我们提供了这些原子操作类

  • 更新基本类型类:AtomicBooleanAtomicIntegerAtomicLongAtomicReference
  • 更新数组类:AtomicIntegerArrayAtomicLongArrayAtomicReferenceArray
  • 更新引用类型:AtomicReferenceAtomicMarkableReferenceAtomicStampedReference
  • 原子更新字段类:AtomicReferenceFieldUpdaterAtomicIntegerFieldUpdaterAtomicLongFieldUpdater

观察这些类的源码我们可以发现,CAS底层的原理实现都需要借助一个Unsafe类来实现,比如对于AtomicInteger类的compareAndSet方法:

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

代码中unsafe变量的初始化:

private static final Unsafe unsafe = Unsafe.getUnsafe();

于是,为了尝试使用CAS在本地操作,模仿了上面的代码和初始化,尝试在本地进行测试,代码如下:

public class CASTest {static volatile long valueOffset;
    // Unsafe类初始化
    static Unsafe unsafe = sun.misc.Unsafe.getUnsafe();


    static {//        initUnsafe();
        try {valueOffset = unsafe.objectFieldOffset
                    (CASTest.class.getDeclaredField("value"));
        } catch (Exception ex) {throw new Error(ex); }
    }
    
    private static void initUnsafe() {try {Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            Unsafe unsafe1 =  (Unsafe) f.get(null);
            unsafe = unsafe1;
        } catch (IllegalAccessException e) {e.printStackTrace();
        } catch (NoSuchFieldException e) {e.printStackTrace();
        }
    }
    
    
    volatile int value;

    public CASTest(int value) {this.value = value;
    }
	
    // 测试cas操作
    public void cas() {System.out.println(unsafe.compareAndSwapInt(this, valueOffset , 5, 10));
        System.out.println("this.value:" + this.value);
        System.out.println(unsafe.compareAndSwapInt(this, valueOffset , 5, 20));
        System.out.println("this.value:" + this.value);
    }
    public static void main(String[] args) {new CASTest(5).cas();
    }
}

但是执行后发现,会报错:

Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.SecurityException: Unsafe
	at sun.misc.Unsafe.getUnsafe(Unsafe.java:90)
	at com.lagou.concurrent.demo.test.CASTest.(CASTest.java:9)

查询相关资料后发现,在使用该getUnsafe方法是,会判断classLoader的类型,如果不是systemClassLoader则会抛出SecurityException(“Unsafe”)异常,所以用户编写的程序使用不了unsafe实例。

那如果我们想本地实现可以怎么办呢?

Unsafe类本地使用方法

下面给出一个本地使用Unsafe类初始化的方法,也是网上使用比较多的方法

private static void initUnsafe() {try {Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            Unsafe unsafe1 =  (Unsafe) f.get(null);
            unsafe = unsafe1;
        } catch (IllegalAccessException e) {e.printStackTrace();
        } catch (NoSuchFieldException e) {e.printStackTrace();
        }
    }

使用该初始化方法更新上面的测试代码:

public class CASTest {static volatile long valueOffset;
    static Unsafe unsafe;


    static {initUnsafe();
        try {valueOffset = unsafe.objectFieldOffset
                    (CASTest.class.getDeclaredField("value"));
        } catch (Exception ex) {throw new Error(ex); }
    }

    private static void initUnsafe() {try {Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            Unsafe unsafe1 =  (Unsafe) f.get(null);
            unsafe = unsafe1;
        } catch (IllegalAccessException e) {e.printStackTrace();
        } catch (NoSuchFieldException e) {e.printStackTrace();
        }
    }
    volatile int value;

    public CASTest(int value) {this.value = value;
    }

    public void cas() {System.out.println(unsafe.compareAndSwapInt(this, valueOffset , 5, 10));
        System.out.println("this.value:" + this.value);
        System.out.println(unsafe.compareAndSwapInt(this, valueOffset , 5, 20));
        System.out.println("this.value:" + this.value);
    }
    public static void main(String[] args) {new CASTest(5).cas();
    }
}

正常执行,且输出结果为:

true
this.value:10
false
this.value:10

对于输出结果,根据CAS原理我们分析可知,初始时,我们赋值value=5,那么第一个CAS操作时valueOffset地址对应的value值=5,与compareAndSwapInt参数里的期望值5匹配,因此CAS操作成功返回true,同时value值被赋为10。同理,第二次CAS操作取valueOffset地址对应的value=10,与方法中的期望值5不匹配,则CAS操作失败返回false,此时value的值仍为10。

如果我们把第二次CAS操作的期望值设成10,那么最终的返回value值会为20。

true
this.value:10
true
this.value:20

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


名称栏目:Java并发编程之CAS和Unsafe类本地使用方法-创新互联
文章地址:http://azwzsj.com/article/gihhg.html