Unsafe类学习

Unsafe

Unfase为我们提供了访问底层的机制,仅供java核心类库使用。

因此普通用户程序无法直接获取其实例,且unsafe的构造方法为私有的,但是我们可以通过反射获取。

1.获取unsafe

Unsafe有一个getUnsafe()方法可以获取实例:

 @CallerSensitive
    public static Unsafe getUnsafe() {
        Class var0 = Reflection.getCallerClass();
        if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
            throw new SecurityException("Unsafe");
        } else {
            return theUnsafe;
        }
    }

其返回的是thUnsafe,private static final Unsafe theUnsafe;,因此我们可以反射获取该实例:

public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Unsafe unsafe = getUnsafe();
        System.out.println(unsafe);
    }

    /**
     * 获取Unsafe实例
     * @return
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    public static Unsafe getUnsafe() throws NoSuchFieldException,IllegalAccessException {
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        return (Unsafe)field.get(null);
    }

2.unsafe创建对象

public class UnSafeDemo {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
        //2.创建对象
        newInstance();
    }


    public static void newInstance() throws NoSuchFieldException, IllegalAccessException, InstantiationException {
        Unsafe unsafe = getUnsafe();
        Book book = (Book) unsafe.allocateInstance(Book.class);
        System.out.println(book.getName()+"\t"+book.getPrice());
    }
}
class Book {
    private String name;
    private Integer price;

    public Book() {
        name="三国演义";
        price=77;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getPrice() {
        return price;
    }

    public void setPrice(Integer price) {
        this.price = price;
    }
}

打印结果为null null,因为:

allocateInstance方法用于创建一个类的实例,但是不会调用这个实例的构造方法,如果这个类还未被初始化,则初始化这个类。

3. 直接内存操作

// 分配内存(堆外)
public native long allocateMemory(long var1);
// 重新分配内存
public native long reallocateMemory(long var1, long var3);
// 内存初始化
public native void setMemory(long var1, long var3, byte var5);
// 内存复制
public native void copyMemory(Object var1, long var2, Object var4, long var5, long var7);
// 清除内存
public native void freeMemory(long var1);

我们可以调用allocateMemory方法为进程分配堆外的本地内存,但因为这部分内存不受JVM管辖范围,因此需要freeMemory方法进行回收

4. CAS操作

JUC中大量运用了CAS操作,CAS操作是juc包的基础。Unsafe中提供了int,long和CAS即Compare and Swap,是一种无锁化的算法,如果非要说是一种锁的化,那就是乐观锁,只要在操作数据的时候才会进行比较更新,比悲观锁处理效率更高。

参数1:要修改的对象

参数2: 偏移量

参数3:期望值oldValue

参数4: 更新的值newValue

public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);

public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

5. 异常抛出

使用Unsafe的throwException不需要再方法上抛出:

unsafe.throwException(new IllegalArgumentException());

6.线程相关

public native void unpark(Object var1);

public native void park(boolean var1, long var2);

public native void monitorEnter(Object var1);

public native void monitorExit(Object var1);

public native boolean tryMonitorEnter(Object var1);

LockSupport中使用了park和unpark:

 public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(false, 0L);
        setBlocker(t, null);
    }

线程需要阻塞时,调用park()方法,线程需要继续运行时,调用unpark()方法
monitorEnter:
锁定对象,必须通过monitorExit方法才能解锁。可重入的。
加了 synchronized 关键字的代码段,生成的字节码文件会多出 monitorentermonitorexit 两条指令

7. putXXX 和getXXX

以int为例:

  • getInt

    • 第一个参数:读取对象
    • 第二个参数:内存偏移地址
  • putInt

    • 第一个参数:修改对象
    • 第二个参数:内存偏移地址
    • 第三个参数:要修改的新值
public native int getInt(Object var1, long var2);

public native void putInt(Object var1, long var2, int var4);
 public static void getAndPut() throws InstantiationException, NoSuchFieldException {
        Unsafe unsafe = getUnsafe();
        Book book =new Book();
        Field field = book.getClass().getDeclaredField("name");
        unsafe.putObject(book,unsafe.objectFieldOffset(field),"新三国演义");
        System.out.println(book.getName());//新三国演义
    }

unsafe.objectFieldOffset(field): 获取对应字段的内存偏移地址

8. volatile相关

getXXXVolatile:以volatile的形式读取内存偏移地址为var2的对象的值。

putXXXVolatile:以volatile的形式修改内存偏移地址为var2的对象的值

既然使用了volatile,就说明该操作保证了有序性和可见性。

public native Object getObjectVolatile(Object var1, long var2);

public native void putObjectVolatile(Object var1, long var2, Object var4);

9.order相关

 public native void putOrderedObject(Object var1, long var2, Object var4);

    public native void putOrderedInt(Object var1, long var2, int var4);

    public native void putOrderedLong(Object var1, long var2, long var4);

保证了有序性的写和修改。仅保证了有序性。因此不保证可见性和原子性。

发布了75 篇原创文章 · 获赞 13 · 访问量 8369

猜你喜欢

转载自blog.csdn.net/weixin_43696529/article/details/105060172