Java使用Unsafe类的示例详解


Posted in Java/Android onSeptember 25, 2021

Unsafe 对象提供了非常底层的,操作内存、线程的方法,相当于开了后门。

在atomic类中CAS实现、LockSupport中park unpark的底层都调用了UnSafe中的方法。

UnSafe并不是说线程不安全,而是说操作内存有可能会造成不安全问题。

当然对于开发人员来说

Unsafe 对象不能直接调用,只能通过反射获得

Java使用Unsafe类的示例详解

通过反射获得Unsafe对象

package com.dongguo.unsafe;

import sun.misc.Unsafe;

import java.lang.reflect.Field;

/**
 * @author Dongguo
 * @date 2021/9/12 0012-21:32
 * @description:
 */
public class UnsafeAccessor {
    static Unsafe unsafe;
    static {
        try {
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            unsafe = (Unsafe) theUnsafe.get(null);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new Error(e);
        }
    }
    static Unsafe getUnsafe() {
        return unsafe;
    }

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

运行结果

sun.misc.Unsafe@7ea987ac

使用Unsafe实现 CAS 操作

package com.dongguo.unsafe;

import lombok.Data;
import sun.misc.Unsafe;

import java.lang.reflect.Field;

/**
 * @author Dongguo
 * @date 2021/9/12 0012-21:32
 * @description:
 */
public class UnsafeAccessor {
    static Unsafe unsafe;
    static {
        try {
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            unsafe = (Unsafe) theUnsafe.get(null);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new Error(e);
        }
    }
    static Unsafe getUnsafe() {
        return unsafe;
    }

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

        Field id = Student.class.getDeclaredField("id");
        Field name = Student.class.getDeclaredField("name");
        // 获得成员变量的偏移量
        long idOffset = unsafe.objectFieldOffset(id);
        long nameOffset = unsafe.objectFieldOffset(name);
        Student student = new Student();
        // 使用 cas 方法替换成员变量的值
        unsafe.compareAndSwapInt(student, idOffset, 0, 20); // 返回 true   0为旧值 20为新值
        unsafe.compareAndSwapObject(student, nameOffset, null, "张三"); // 返回 true 旧值为null,新值为张三
        System.out.println(student);
    }
}
@Data
class Student {
    volatile int id;
    volatile String name;
}

运行结果

sun.misc.Unsafe@7ea987ac
Student(id=20, name=张三)

直接使用Unsafe类实现之前AtomicIntegerFieldUpdater中线程安全的原子整数 BankAccount

在atomic中使用AtomicIntegerFieldUpdater实现money线程安全的原子整数

package com.dongguo.unsafe;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

/**
 * @author Dongguo
 * @date 2021/9/7 0007-14:41
 * 以一种线程安全的方式操作非线程安全对象的某些字段。
 * 需求:
 * 1000个人同时向一个账号转账一元钱,那么累计应该增加1000元,
 * 除了synchronized和CAS,还可以使用AtomicIntegerFieldUpdater来实现。
 */
class BankAccount {
    private String bankName = "ACBC";
    public volatile int money = 0;
    AtomicIntegerFieldUpdater<BankAccount> fieldUpdater = AtomicIntegerFieldUpdater.newUpdater(BankAccount.class, "money");

    public void transferMoney(BankAccount bankAccount) {
        fieldUpdater.incrementAndGet(bankAccount);
    }
}

public class AtomicIntegerFieldUpdaterDemo {
    public static void main(String[] args) {
        BankAccount bankAccount = new BankAccount();

        for (int i = 1; i <= 1000; i++) {
            new Thread(() -> {
                bankAccount.transferMoney(bankAccount);
            }, String.valueOf(i)).start();
        }
        //暂停毫秒
        try {
            TimeUnit.MILLISECONDS.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(bankAccount.money);
    }

}

改为使用UnSafe实现money线程安全的原子整数

package com.dongguo.unsafe;

import sun.misc.Unsafe;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

/**
 * @author Dongguo
 * @date 2021/9/7 0007-14:41
 */
class BankAccount {
    private String bankName = "ACBC";
    public volatile int money;
    static final Unsafe unsafe;
    static final long DATA_OFFSET;

    static {
        unsafe = UnsafeAccessor.getUnsafe();
        try {
            // money 属性在 BankAccount 对象中的偏移量,用于 Unsafe 直接访问该属性
            DATA_OFFSET = unsafe.objectFieldOffset(BankAccount.class.getDeclaredField("money"));
        } catch (NoSuchFieldException e) {
            throw new Error(e);
        }
    }

    public BankAccount(int money) {
        this.money = money;
    }

    public void transferMoney(int amount) {
        int oldValue;
        while (true) {
            // 获取共享变量旧值,可以在这一行加入断点,修改 data 调试来加深理解
            oldValue = money;
            // cas 尝试修改 data 为 旧值 + amount,如果期间旧值被别的线程改了,返回 false
            if (unsafe.compareAndSwapInt(this, DATA_OFFSET, oldValue, oldValue + amount)) {
                return;
            }
        }
    }
}
public class AtomicIntegerFieldUpdaterDemo {
    public static void main(String[] args) {
        BankAccount bankAccount = new BankAccount(0);

        for (int i = 1; i <= 1000; i++) {
            new Thread(() -> {
                bankAccount.transferMoney(1);
            }, String.valueOf(i)).start();
        }
        //暂停毫秒
        try {
            TimeUnit.MILLISECONDS.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(bankAccount.money);
    }
}
运行结果
1000
/暂停毫秒
        try {
            TimeUnit.MILLISECONDS.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(bankAccount.money);
    }
}

运行结果

1000

到此这篇关于Java使用Unsafe类的文章就介绍到这了,更多相关Java使用Unsafe类内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Java/Android 相关文章推荐
新手入门Jvm-- JVM对象创建与内存分配机制
Jun 18 Java/Android
Java数据结构之链表相关知识总结
Jun 18 Java/Android
Java基于字符界面的简易收银台
Jun 26 Java/Android
分析Java中Map的遍历性能问题
Jun 26 Java/Android
SpringBoot实现异步事件驱动的方法
Jun 28 Java/Android
Springboot使用Spring Data JPA实现数据库操作
Jun 30 Java/Android
分析ZooKeeper分布式锁的实现
Jun 30 Java/Android
SpringBoot SpringEL表达式的使用
Jul 25 Java/Android
解析mybatis-plus中的resultMap简单使用
Nov 23 Java/Android
Java9新特性之Module模块化编程示例演绎
Mar 16 Java/Android
Java 关于String字符串原理上的问题
Apr 07 Java/Android
Java 垃圾回收超详细讲解记忆集和卡表
Apr 08 Java/Android
Spring-cloud Config Server的3种配置方式
Sep 25 #Java/Android
MyBatis-Plus 批量插入数据的操作方法
Sep 25 #Java/Android
spring cloud 配置中心native配置方式
Sep 25 #Java/Android
spring cloud 配置中心客户端启动遇到的问题
Sep 25 #Java/Android
SpringBoot+Vue+JWT的前后端分离登录认证详细步骤
Sep 25 #Java/Android
java如何实现socket连接方法封装
Sep 25 #Java/Android
IDEA2021.2配置docker如何将springboot项目打成镜像一键发布部署
You might like
建立动态的WML站点(三)
2006/10/09 PHP
PHP利用str_replace防注入的方法
2013/11/10 PHP
PHP处理数组和XML之间的互相转换
2016/06/02 PHP
PHP编程计算两个时间段是否有交集的实现方法(不算边界重叠)
2017/05/30 PHP
JavaScript Cookie显示用户上次访问的时间和次数
2009/12/08 Javascript
js实现的日期操作类DateTime函数代码
2010/03/16 Javascript
javascript 节点遍历函数
2010/03/28 Javascript
jquery foreach使用示例
2013/09/12 Javascript
文本框回车提交与禁止提交示例
2013/09/27 Javascript
浅析JavaScript基本类型与引用类型
2014/05/28 Javascript
JavaScript中的闭包(Closure)详细介绍
2014/12/30 Javascript
js实现页面跳转的五种方法推荐
2016/03/10 Javascript
slideToggle+slideup实现手机端折叠菜单效果
2017/05/25 Javascript
记一次webpack3升级webpack4的踩坑经历
2018/06/12 Javascript
JS学习笔记之数组去重实现方法小结
2019/05/29 Javascript
微信小程序—setTimeOut定时器的问题及解决
2019/07/26 Javascript
layui的面包屑或者表单不显示的解决方法
2019/09/05 Javascript
vue中el-input绑定键盘按键(按键修饰符)
2020/07/22 Javascript
CentOS 7 安装python3.7.1的方法及注意事项
2018/11/01 Python
python 实现快速生成连续、随机字母列表
2019/11/28 Python
Python3打包exe代码2种方法实例解析
2020/02/17 Python
Python3 pywin32模块安装的详细步骤
2020/05/26 Python
python实现斗地主分牌洗牌
2020/06/22 Python
python cookie反爬处理的实现
2020/11/01 Python
中国最大的潮流商品购物网站:YOHO!BUY有货
2017/01/07 全球购物
Skyscanner台湾:全球知名的旅行比价引擎
2018/07/01 全球购物
精伦电子Java笔试题
2013/01/16 面试题
中学生的1000字检讨书
2014/10/11 职场文书
2015年全国“爱牙日”宣传活动总结
2015/03/23 职场文书
会计出纳岗位职责
2015/03/31 职场文书
文明旅游倡议书
2015/04/28 职场文书
2016年父亲节寄语
2015/12/04 职场文书
导游词之舟山普陀山
2019/11/06 职场文书
2019年公司快递收发管理制度模板
2019/11/20 职场文书
利用 SQL Server 过滤索引提高查询语句的性能分析
2021/07/15 SQL Server
德生2P3收音机开箱评测
2022/04/30 无线电