AQS研究系列(一)--Unsafe使用

前言

为了研究AQS,我们先来学习下java中cas(Compare And Swap)的基础Unsafe类的使用

概念

Unsafe产生于java无法向c那样操作底层操作系统,但一些场景又需要相关操作.所以此类提供了一些java语言对于操作系统内存层面操作的API.这显然被认为是不安全的,所以此类是不公开的,不建议被java应用直接使用.
但现实中已经有大量的java并发相关操作的框架在使用它了....据说此类在计划废弃中.

使用

Unsafe能操作内存?这个是什么概念?都有哪些操作呢?

其实最明显的是它大量方法都是直接操作内存地址进行操作的.方法可以分为下面几类:

  1. 自己引用测试类:
    因Unsafe方法的不安全性(比如可以直接操作内存,但jvm无法管理,造成oom),所以我们只能通过反射的方法进行使用:
        Field f = Unsafe.class.getDeclaredField("theUnsafe");
        f.setAccessible(true);
        Unsafe unsafe = (Unsafe) f.get(null);
  1. 线程操作类:

我们可以使用LockSupport类进行操作

a. LockSupport.park()对应Unsafe的Unsafe.park(false, 0L)------>给当前所在线程加锁,第一个参数表示true为精度型单位为纳秒,false单位毫秒,第二次参数表示等待时间;

b. LockSupport.park.unpark --------->Thread thread对应Unsafe的UNSAFE.unpark(thread)方法(解锁指定线程)
如果,我们直接使用Unsafe,是这样子的:


    public static void main(String[] args)
        throws IllegalAccessException, NoSuchFieldException, IOException, InterruptedException {
        Field f = Unsafe.class.getDeclaredField("theUnsafe");
        f.setAccessible(true);

        Unsafe unsafe = (Unsafe) f.get(null);

        Thread thread=new Thread(
            ()->{
                unsafe.park(false,0l);
                System.out.println("线程一执行");
            }
        );
        thread.start();

        Thread thread2=new Thread(
            ()->{
                unsafe.park(false,0l);
                System.out.println("线程二执行");
            }
        );
        thread2.start();
        TimeUnit.SECONDS.sleep(1l);
        unsafe.unpark(thread2);
        unsafe.unpark(thread);
        System.in.read();
    }
    
  1. 对象属性操作类:

我们还可以通过Unsafe类获取对象的属性值.因为Unsafe类是直接操作内存的,所以需要我们获得对应的属性内存地址,如下操作:

 private static int ASHIFT;
    private static long ABASE;

    public static void main(String[] args)
        throws IllegalAccessException, NoSuchFieldException, IOException, InterruptedException {

            //1。获取unsafe对象
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            Unsafe unsafe = (Unsafe) f.get(null);

            //2.拒两个我们测试用的数据对象
            String[] array1 = new String[]{"fd", "sds", "hffij", "er", "er", "sds"};
            String[] array2 = new String[]{"ff", "fd", "fdf", "xcv", "xcv", "cv"};

            //3。获取String[].class类的内存地址
            Class<?> ak = String[].class;
            ABASE = unsafe.arrayBaseOffset(ak);

            //4.获取此数组的每个成员的内存偏移量(就是每个对象占内存大小)
            int scale = unsafe.arrayIndexScale(ak);
            ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);

            //5。获取对应位置的属性,如2 << ASHIFT,表示位置为2的数据(2*偏移量)
            String array11 = (String) unsafe.getObject(array1, ((long) 2 << ASHIFT) + ABASE);
            String array21 = (String) unsafe.getObject(array2, ((long) 5 << ASHIFT) + ABASE);

            System.out.println(ABASE);
            System.out.println(scale);
            System.out.println(ASHIFT);
            System.out.println(array11);
            System.out.println(array21);
        }

  1. cas原子操作类:

如下操作,通过unsafe类实现cas原子操作.


    static Unsafe UNSAFE = null;


    private Object test1;


    public Unsafe3 setTest1(Object test1) {
        this.test1 = test1;
        return this;
    }

    private static long test1Index;

    static {

        try {
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);

            UNSAFE = (Unsafe) f.get(null);

            //获取Unsafe3对象内存位置
            Class u = Unsafe3.class;

            //获取此对象中test1属性内存偏移
            test1Index = UNSAFE.objectFieldOffset
                (u.getDeclaredField("test1"));

        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }


    }

    public static void main(String[] args) {

        Object o = new Object();
        Unsafe3 unsafe3 = new Unsafe3();

        //test1属性赋值
        unsafe3.setTest1(o);
        Object o2 = new Object();

        //cas操作,匹配原有属性是否为o,是则赋新值,返回ture
        boolean b = UNSAFE.compareAndSwapObject(unsafe3, test1Index, o2, o);
        System.out.println(b);

        boolean b2 = UNSAFE.compareAndSwapObject(unsafe3, test1Index, o, o2);
        System.out.println(b2);

    }

总结

好了,上面就是unsafe的基本几种使用,其也是aqs框架中cas操作的基础.下面我们进行aqs相关学习.

AQS研究系列(二)--线程状态和interrupt()、interrupted()、isInterrupted等方法学习
AQS研究系列(三)--AbstractQueuedSynchronizer源码分析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容