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源码分析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容