专栏首页好派笔记为什么final引用不能从构造函数内“逸出”
原创

为什么final引用不能从构造函数内“逸出”

  前面我们提到过,写final域的重排序规则可以确保:在引用变量为任意线程可见之前,该引用变量指向的对象的final域已经在构造函数中被正确初始化过了。其实要得到这个效果,还需要一个保证:在构造函数内部,不能让这个被构造对象的引用为其他线程可见,也就是对象引用不能在构造函数中“逸出”。为了说明问题,让我们来看下面示例代码:

public class FinalReferenceEscapeExample {
	final int i;
	static FinalReferenceEscapeExample obj;

	public FinalReferenceEscapeExample () {
		i = 1;                              //1写final域
		obj = this;                          //2 this引用在此“逸出”
	}

	public static void writer() {
		new FinalReferenceEscapeExample ();
	}

	public static void reader() {
		if (obj != null) {                     //3
			int temp = obj.i;                 //4
		}
	}
}

  假设一个线程A执行writer()方法,另一个线程B执行reader()方法。这里的操作2使得对象还未完成构造前就为线程B可见。即使这里的操作2是构造函数的最后一步,且即使在程序中操作2排在操作1后面,执行read()方法的线程仍然可能无法看到final域被初始化后的值,因为这里的操作1和操作2之间可能被重排序。实际的执行时序可能如下图所示:

final关键字的好处

  • 将类、方法、变量声明为final能够提高性能,这样JVM就有机会进行分析优化。比如被final修饰的方法,JVM会尝试为之寻求内联,这对于提升Java的效率是非常重要的。因此,假如能确定方法不会被继承,那么尽量将方法定义为final的。
  • 被final修饰的常量,在编译阶段会存入调用类的常量池中(比如子类继承父类,那么父类的final常量会被复制到子类常量池中),当子类使用这个常量时,不会引起父类的初始化。
  • final可以用于不可变对象的创建,而不可变对象一定是线程安全的。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

关注作者,阅读全部精彩内容

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 为什么构造函数不能为虚函数

    1、从使用角度         虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用。构造函数本身就是要初始化实例,那使用虚函数也没有实际意义呀。所以...

    用户1215536
  • JAVA中的内存语义

    当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存中。

    Montos
  • Java并发编程——this引用逸出("this"Escape)

    并发编程实践中,this引用逃逸("this"escape)是指对象还没有构造完成,它的this引用就被发布出去了。这是危及到线程安全的,因为其他线程有可能通过...

    用户7886150
  • 深入理解Java内存模型(六)——final

    小小明童鞋
  • ​JMM中的final关键字解析

    与前面介绍的锁和volatile相比较,对final域的读和写更像是普通的变量访问。对于final域,编译器和处理器要遵守两个重排序规则:

    Java技术江湖
  • final关键字详解

    final修饰的类不能被继承,修饰的方法不能被重写,修饰的变量不能被二次赋值,总之,final就是最终的意思,保证了不变性。除了对不变性的保障,对有序性fina...

    naget
  • 并发编程中的逃离“996icu”——this引用逃逸

    并发编程实践中,this引用逃逸("this"escape)是在构造器构造还未彻底完成前(即实例初始化阶段还未完成),将自身this引用向外抛出并被其他线程复制...

    23号杂货铺
  • Java线程安全性中的对象发布和逸出

    发布(Publish)和逸出(Escape)这两个概念倒是第一次听说,不过它在实际当中却十分常见,这和Java并发编程的线程安全性就很大的关系。 什么是发布?简...

    用户1148394
  • Java安全发布对象总结-0

    在类的外部线程都能访问到这个state,这样发布对象是不安全,我们无法保证外部的线程不去修改state,从而造成state状态的错误。

    用户2032165
  • 发布与逸出

    SuperHeroes
  • Java并发编程(3)- 如何安全发布对象

    在这个例子中,我们通过new对象得到了对象实例。获得这个对象后,我们可以调用getStates()方法得到私有属性的引用,这样就可以在其他任何线程中,修改该属性...

    端碗吹水
  • 讲真,我发现这本书有个地方写错了!

    我手上这本《Java并发编程的艺术》的版次是:2019年3月第1版第14次印刷。

    why技术
  • 为什么不允许使用 Java 静态构造函数?

    不允许使用 Java 静态构造函数,但是为什么呢?在深入探讨不允许使用静态构造函数的原因之前,让我们看看如果要使 构造函数静态化 会发生什么。

    淡定的蜗牛
  • Java并发编程实战 02Java如何解决可见性和有序性问题

    在上一篇文章[Java并发编程实战 01并发Bug的源头](https://mp.weixin.qq.com/s/QT44HS47l_ir08pCZeFU5Q)...

    Johnson木木
  • 并发学习笔记09-final域的内存语义

    final域,编译器和处理器要遵守两个重排序规则: - 在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不...

    WindCoder
  • Java的final

     谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字。另外,Java中的String类就是一个final类,那么今天...

    用户3467126
  • java并发编程读书笔记(1)-- 对象的共享

    1. 一些原则 RIM(Remote Method Invocation):远程方法调用 Race Condition:竞态条件 Servlet要满足多个线程的...

    Ryan-Miao
  • 对象共享:Java并发环境中的烦心事

    并发的意义在于多线程协作完成某项任务,而线程的协作就不可避免地需要共享数据。今天我们就来讨论下如何发布和共享类对象,使其可以被多个线程安全地访问。

    lyb-geek
  • Java-安全发布

     发布是一个动词,是去发布对象。而对象,通俗的理解是:JAVA里面通过 new 关键字 创建一个对象。

    Fisherman渔夫

作者介绍

好派笔记

秦皇岛经济技术开发区易云软件开发服务部技术总监

秦皇岛经济技术开发区易云软件开发服务部 · 技术总监 (已认证)

专栏

精选专题

活动推荐

扫码关注云+社区

领取腾讯云代金券