final的安全发布

2023-12-16

final的安全发布

两个关键字“发布”“安全”
所谓发布通俗一点的理解就是创建一个对象,使这个对象能被当前范围之外的代码所使用
比如Object o = new Object();
然后接下来使用对象o
但是对于普通变量的创建,之前分析过,大致分为三个步骤:
1、分配内存空间
2、将o指向分配的内存空间
3、调用构造函数来初始化对象
这三个步骤不是原子的,如果执行到第二步,还没有进行初始化,此时对象已经不是null了,如果被其他代码访问,这将收获一个错误的结果。
或者说对象尚未完全创建就被使用了,其他线程看到的结果可能是不一致的,这就是不安全的发布
根本原因就是JVM创建对象的过程涉及到分配空间、指针设置、数据初始化等步骤,并不是同步的,涉及到主存与缓存、处理器与寄存器等,可见性没办法得到保障
所以说,什么是安全发布,简单理解就是对象的创建能够保障在被别人使用前,已经完成了数据的构造设置,或者说一个对象在使用时,已经完成了初始化。
不幸的是,Java对此并没有进行保障,你需要自己进行保障,比如synchronized关键字,原子性、排他性就可以做到这一点

不安全的发布实例

怎么保障安全发布?有几种方法:

一种是刚才提到的锁机制,通过加锁可以保障中间状态不会被读取
另外还有:
1、借助于volatile或者AtomicReference声明对象
2、借助于final关键字
3、在静态初始化块中,进行初始化(JVM会保障)
4、将对象引用保存到一个由锁保护的域中
5、借助AtomicReference

很显然,对于锁机制,那些线程安全的容器比如ConcurrentMap,也是满足这条的,所以也是安全发布
对于final,当你创建一个对象时,使用final关键字能够使得另一个线程不会访问到处于“部分创建”的对象
因为:当构造函数退出时,final字段的值保证对访问构造对象的其他线程可见
如果某个成员是final的,JVM规范做出如下明确的保证:
一旦对象引用对其他线程可见,则其final成员也必须正确的赋值
所以说借助于final,就如同你对对象的创建访问加锁了一般,天然的就保障了对象的安全发布。
对于普通的变量,对象的内存空间分配、指针设置、数据初始化,和将这个变量的引用赋值给另一个引用,之间是可能发生重排序的,所以也就导致了其他线程可能读取到不一致的中间状态
但是对于final修饰的变量,JVM会保障顺序
不会在对final变量的写操作完成之前,与将变量引用赋值给其他变量之间进行重排序,也就是final变量的设置完成始终会在被读取之前
final除了不可变的定义之外,还与线程安全发布息息相关
借助于final,可以达到对象安全发布的保障,只需要借助于final,不在需要任何额外的付出,他能够保障在多线程环境下,总是能够读取到正确的初始化的值
所以,如果你不希望变量后续被修改,你应该总是使用final关键字
而且,很显然在某些场景下,final也可以解决一定的安全问题

实例

使用synchronized锁的时候,作为锁的对象最好要加上final修饰符,因为可能线程会改变锁变量持有的具体的对象。
demo如下:

public class Test02 {
static Object lock = new Object();

public static void main(String[] args) {
    Thread t1 = new Thread(() -> {
        lock = new Object();
        synchronized (lock) {
            for (int i = 0; i < 10; i++) {
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("A");
            }
        }
    });

    Thread t2 = new Thread(() -> {
        lock = new Object();
        synchronized (lock) {
            for (int i = 0; i < 10; i++) {
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("B");
            }
        }
    });

    t1.start();
    t2.start();
}

}

但是要是把锁改成final的。代码如下:

public class Test02 {
    static final Object lock = new Object();

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
//            lock = new Object(); // 编译出错,final不能修改
            synchronized (lock) {
                for (int i = 0; i < 10; i++) {
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("A");
                }
            }
        });

        Thread t2 = new Thread(() -> {
//            lock = new Object();
            synchronized (lock) {
                for (int i = 0; i < 10; i++) {
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("B");
                }
            }
        });

        t1.start();
        t2.start();
    }
}

实例参考:https://juejin.cn/post/7104070219806539806
原理参考:https://www.cnblogs.com/noteless/p/10416678.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

final的安全发布 的相关文章

随机推荐

  • Linux查看进程

    文章目录 查看进程 top命令 pstree命令 查看进程 1 ps命令 用于显示当前系统中正在运行的进程列表 例如 ps aux 查看系统所有进程 ps aux命令展示的各字段含义如下 USER 进程的用户 PID 进程ID CPU 进程
  • net::ERR_CERT_AUTHORITY_INVALID

    接口请求报错 net ERR CERT AUTHORITY INVALID 浏览器遇到这种不安全的证书会自动拦截 导致数据无法获取 问题分析 翻译一下 其实从字面意思就可以指定报错原因了 https的ssl证书有问题 解决办法 临时解决办法
  • harbor企业级私有仓库

    一 私有仓库的部署 实验环境 删除之前的registry容器 并下载harbor二进制程序和压缩包 官网地址 https github com 部署步骤 1 下载压缩包和脚本并解开压缩包并进入harbor目录并复制模板文件 系统读的是yml
  • SNMP的配置

    SNMP的配置与日志 SNMP 配置文件 大多数系统上的 SNMP 配置信息存储在配置文件中 通常 SNMP 的主要配置文件是 etc snmp snmpd conf 或 etc snmp snmpd conf 你可以使用文本编辑器查看这些
  • go-zero 开发之安装 goctl 及 go-zero 开发依赖

    安装 goctl go 版本在 1 16 及以后执行 GO111MODULE on go install github com zeromicro go zero tools goctl latest go 版本在 1 16 之前执行 GO
  • 用RPA轻松实现天猫物流-延迟报备-紧急报备,提升处理效率!

    商业活动中的物流管理的重要性不言而喻 及时准确的物流信息可以提高客户满意度 增加用户粘性 促进二次购买和口碑传播 同时 高效的物流管理也可以减少商家的成本和风险 提高运营效率和竞争力 在天猫物流管理中 延迟报备指的是订单的物流信息没有及时更
  • Docker仓库详解及搭建

    一 Docker仓库是什么 二 Registry工作原理 Pull Image 用户端向index发送请求 index会返回一个location和token 用户端根据返回的token向registry发起请求 registry会校验一笑t
  • 解放双手!拼多多商家最新秘密武器,微信端批量私信rpa机器人来袭!

    在拼多多开店的卖家们都知道 拼多多商家在进行拓客引流工作时 需要频繁进行微信端私信发送操作 耗费大量时间和精力 为了解决这一问题 商家希望可以通过rpa机器人来自动完成私信发送操作 保证每一笔订单都能做好全面的维护和管理 八爪鱼rpa作为一
  • Springboot+FastJson实现解析第三方http接口json数据为实体类(时间格式化转换、字段包含中文)

    场景 若依前后端分离版手把手教你本地搭建环境并运行项目 若依前后端分离版手把手教你本地搭建环境并运行项目 前后端分离项目本地运行 CSDN博客 在上面搭建SpringBoot项目的基础上 并且在项目中引入fastjson hutool lo
  • 四种数据库执行脚本文件导入数据的方式

    执行脚本文件的方式 Mysql mysql执行sql脚本文件的方法 1 在命令行输入mysql uroot h10 235 5 55 p 123456 P3306 lt F hello niuzi sql 2 在命令行输入 source F
  • maven上传jar包到代码仓库

    一 前言 一般被引用的包开发都是要求放在nexus仓库中 等到有jar包服务需要引用该包的时候直接从nexus仓库中获取即可 实现了该引用包的公用 二 代码配置 编辑代码中的pom xml文件配置 vi pom xml
  • Sybase死锁问题查询与解决

    Sybase死锁问题查询与解决 sp who 查看锁表情况 sp lock 查看被锁的表的id号 查看数据库lock配置 sp config number of lock 数据库锁资源使用情况 sp lock 检查锁资源使用情况 selec
  • 用RPA轻松获取亚马逊销售订单详细信息,提升业务效率!

    在电商行业中 获取销售订单的详细信息是一项重要且繁琐的任务 传统的方法是手动登录亚马逊平台 逐个查看订单并复制粘贴相关信息 这不仅耗费大量时间和人力资源 还容易出现错误和遗漏 八爪鱼rpa作为一款强大的机器人流程自动化工具可以帮助企业自动化
  • Linux环境变量执行顺序

    环境变量执行顺序 etc profile etc profile d sh bash profile bashrc etc bashrc
  • 生意参谋竞品分析RPA机器人,让你在商战中立于不败之地

    作为电商企业 了解竞争对手的动态和策略对于制定有效的竞争策略至关重要 但是竞对分析是一项繁琐而费时的工作 往往需要大量的人力和时间投入 在这样的情况下 八爪鱼rpa机器人的出现为电商企业带来了新的解决方案 rpa机器人是一种基于自动化软件的
  • mysql执行带函数命令的sql脚本报错

    一 前言 开发给了一个带函数的sql文件让我执行 但是执行导入时报以下错误 This function has none of DETERMINISTIC NO SQL or READS SQL DATA in its declaratio
  • 万字整理Redis核心知识点

    1 Redis介绍 Redis 是 NoSQL 但是可处理 1 秒 10w 的并发 数据都在内存中 使用 java 对 redis 进行操作类似 jdbc 接口标准对 mysql 有各类实现他的实现类 我们常用的是 druid 其中对 re
  • mysql开启查询日志

    mysql默认不开启查询日志 可以通过命令查询 show VARIABLES LIKE general 开启查询日志 并更改日志存放目录 不过存放的目录一定要有权限不然会报错 手动创建一下log目录下的mysql目录并赋予权限 mkdir
  • 客户案例 | 博睿数据全面保障昆仑银行业务稳定性

    新兴市场和不断增长的客户群体需求的崛起 正推动着基于互联网模式的财富陪伴 财富管理和财富生态的全新业务范式的形成 昆仑银行是一家总部位于北京 分支机构遍布全国性的城商行 提供广泛的金融产品和服务 主要包括个人银行业务 企业金融服务 资产管理
  • final的安全发布

    final的安全发布 两个关键字 发布 安全 所谓发布通俗一点的理解就是创建一个对象 使这个对象能被当前范围之外的代码所使用 比如Object o new Object 然后接下来使用对象o 但是对于普通变量的创建 之前分析过 大致分为三个