并发编程之Unsafe魔法类详解

1. unsafe类详解

Unsafe是位于sun.misc包下的一个类,主要提供一些用于执行低级别、不安全操作的方法,如直接访问系统内存资源、自主管理内存资源等,这些方法在提升Java运行效率、增强Java语言底层资源操作能力方面起到了很大的作用。但由于Unsafe类使Java语言拥有了类似C语言指针一样操作内存空间的能力,这无疑也增加了程序发生相关指针问题的风险。

在程序中过度、不正确使用Unsafe类会使得程序出错的概率变大,使得Java这种安全的语言变得不再“安全”,因此对Unsafe的使用一定要慎重。Unsafe类为一单例实现,提供静态方法getUnsafe获取Unsafe实例,当且仅当调用getUnsafe方法的类为引导类加载器所加载时才合法,否则抛出SecurityException异常。

2.如何获取Unsafe实例?

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

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

2、通过反射获取单例对象theUnsafe。

public class UnsafeInstance {
    public static Unsafe reflectGetUnsafe() {
        try {
    Field field =Unsafe.class.getDeclaredField("theUnsafe"); 
            field.setAccessible(true);
            return (Unsafe) field.get(null);
        } catch (Exception e) {           
            e.printStackTrace(); 
        }return null;  
    }
}

3.unsafe功能简介(CAS)

3.1 以下代码主要为CAS相关操作的方法

/** *  CAS * @param o         包含要修改field的对象 
* @param offset    对象中某field的偏移量 
* @param expected  期望值 
* @param update    更新值 
* @return          true | false 
*/
public final native boolean compareAndSwapObject(Object var1, longvar2, Object var4, Object var5);
public final native boolean compareAndSwapInt(Object var1, longvar2, int var4, int var5);
public final native boolean compareAndSwapLong(Object var1, longvar2, long var4, long var6);

3.2 以下代码为线程调度

//取消阻塞线程
public native void unpark(Object thread);
//阻塞线程
public native void park(boolean isAbsolute, long time);
//获得对象锁(可重入锁)
@Deprecated
public native void monitorEnter(Object o);
//释放对象锁
@Deprecated
public native void monitorExit(Object o);
//尝试获取对象锁
@Deprecated
public native boolean tryMonitorEnter(Object o);

方法park、unpark即可实现线程的挂起与恢复,将一个线程进行挂起是通过park方法实现的,调用park方法后,线程将一直阻塞直到超时或者中断等条件出现;

unpark可以终止一个挂起的线程,使其恢复正常。

典型应用

Java锁和同步器框架的核心类AbstractQueuedSynchronizer,就是通过调用LockSupport.park()和LockSupport.unpark()

实现线程的阻塞和唤醒的,而LockSupport的park、unpark方法实际是调用Unsafe的park、unpark方式来实现;

3.3、内存屏障

在Java 8中引入,用于定义内存屏障(也称内存栅栏,内存栅障,屏障指令等,是一类

同步屏障指令,是CPU或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的

所有读写操作都执行后才可以开始执行此点之后的操作),避免代码重排序。

//内存屏障,禁止load操作重排序。屏障前的load操作不能被重排序到屏障后,屏障后的load操作不能被重排序到屏障前

public native void loadFence();

//内存屏障,禁止store操作重排序。屏障前的store操作不能被重排序到屏障后,屏障后的store操作不能被重排序到屏障前

public native void storeFence();

//内存屏障,禁止load、store操作重排序

public native void fullFence();

典型应用

在Java 8中引入了一种锁的新机制——StampedLock,它可以看成是读写锁的一个改进版本。StampedLock提供了一种乐观读锁的实现,这种乐观读锁类似于无锁的操作,完全不会阻塞写线程获取写锁,从而缓解读多写少时写线程“饥饿”现象。由于StampedLock提供的乐观读锁不阻塞写线程获取读锁,当线程共享变量从主内存load到线程工作内存时,会存在数据不一致问题,所以当使用StampedLock的乐观读锁时,需要遵从如下图用例中使用的模式来确保数据的一致性。

猜你喜欢

转载自blog.csdn.net/qq_38130094/article/details/104156504