您的位置:首页 > Web前端

JAVA UnSafe & CAS & AtomicInteger

2017-02-21 10:44 274 查看
package com.shob.unsafe;

public class UnSafeDesc {

/**
* UnSafe 的作用
* 		可以用来在任意内存地址位置处读写数据,可见,对于普通用户来说,使用起来还是比较危险的;
*/

/**
* java无法直接访问到操作系统层
* 而UnSafe提供了硬件级别的原子操作。
*
* 首先介绍一下什么是Compare And Swap(CAS)?简单的说就是比较并交换。
CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。
如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。
否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。
CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;
否则,不要更改该位置,只告诉我这个位置现在的值即可。”
Java并发包(java.util.concurrent)中大量使用了CAS操作,涉及到并发的地方都调用了sun.misc.Unsafe类方法进行CAS操作。
*
*/

/**
* 比较obj的offset处内存位置中的值和期望的值,如果相同则更新。此更新是不可中断的。
*
* @param obj 需要更新的对象
* @param offset obj中整型field的偏移量
* @param expect 希望field中存在的值
* @param update 如果期望值expect与field的当前值相同,设置filed的值为这个新值
* @return 如果field的值被更改返回true
*/
public native boolean compareAndSwapInt(Object obj, long offset, int expect, int update);

//CAS操作有3个操作数,内存值M,预期值E,新值U,如果M==E,则将内存值修改为B,否则啥都不做。

}


  

getLongVolatile/putLongVolatile等等方法

  这类方法使用volatile语义去存取数据,差不多就是各个线程不缓存数据,直接在内存中读取数据;

获取Unsafe对象

  遗憾的是,Unsafe对象不能直接通过
new Unsafe()
或调用
Unsafe.getUnsafe()
获取,原因如下:

    *不能直接
new Unsafe()
,原因是
Unsafe
被设计成单例模式,构造方法是私有的;

    *不能通过调用
Unsafe.getUnsafe()获取,因为
getUnsafe
被设计成只能从引导类加载器(bootstrap class loader)加载。


  但得Unsafe类中有个私有的静态全局属性
theUnsafe(Unsafe实例对象)
,通过反射,可以获取到该成员属性theUnsafe对应的Field对象,并将其设置为可访问,从而得到theUnsafe具体对象。

AtomicInteger

  提供原子性操作的数值操作类,底层采用UnSafe & CAS机制,通过当前值进行与旧值进行对比,如果一样就将新值替换内存中的旧值,反之返回旧值。

  AtomicInteger能保证自身的自增操作和加减操作为原子操作,是因为第一采用了CAS机制,在值判断和交换上会保证一致,第二本身value值定义为volatile,

而volatile保证可见性,这样就能保证操作为原子操作。

原理:

  Java本身并不具备直接访问底层操作系统的能力,但是还是提供了一组可以访问底层方法的类,Unsafe中含有一系列的native本地方法,用于一些特殊的操作。

  比如获取某个值的内存地址,替换新的值等等。

  java.util.concurrent包建立在CAS(比较与交换)上,而CAS的核心是Unsafe。所以CAS的操作原理,CAS操作数有三个值,预期值、旧值、新值,将预期值与旧值进行比较,如果预期值等于旧值,

就将内存值修改为新值,否则就什么都不干,返回false。内部的交换可以理解为原子操作,比较+交换虽为2步操作,但加锁之后同时间只有一个可访问。

  

  

CAS的缺点

CAS看起来很美,但这种操作显然无法涵盖并发下的所有场景,并且CAS从语义上来说也不是完美的,存在这样一个逻辑漏洞:如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然是A值,那我们就能说明它的值没有被其他线程修改过了吗?如果在这段期间它的值曾经被改成了B,然后又改回A,那CAS操作就会误认为它从来没有被修改过。这个漏洞称为CAS操作的"ABA"问题。java.util.concurrent包为了解决这个问题,提供了一个带有标记的原子引用类"AtomicStampedReference",它可以通过控制变量值的版本来保证CAS的正确性。不过目前来说这个类比较"鸡肋",大部分情况下ABA问题并不会影响程序并发的正确性,如果需要解决ABA问题,使用传统的互斥同步可能回避原子类更加高效。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: