精华内容
下载资源
问答
  • 为什么说 "构造函数中this引用逸出的问题"在构造函数中, this引用逸出, 则此时 可能对象实例还没有完全初始化. 比如此对象实例有final int i=47; 因为i还没有初始化, 外部线程去 查看i, 可能会看到i==null的结果. 这...

    谈谈自己的理解, 权作 抛砖引玉.

    为什么说 "构造函数中this引用逸出的问题"

    在构造函数中, this引用逸出, 则此时 可能对象实例还没有完全初始化. 比如此对象实例有final int i=47; 因为i还没有初始化, 外部线程去 查看i, 可能会看到i==null的结果. 这就不对了

    说仔细一点, 初始化一个对象, 比如代码new TestClass(), 完成了以下操作

    1). 为TestClass对象实例分配内存空间; this指针指向此空间;

    2). 调用TestClass的初始化函数; 在TestClass的初始化函数中;

    2.1). 首先调用TestClass的父类的初始化函数, 这里是一个递归调用父类初始化函数的过程;

    2.2). 执行TestClass自身的初始化逻辑, 如给 对象实例成员 赋初值.

    所以说, 在1) 和 2.2) 之间是有 "空窗期"的, 如果这是this被外部线程拿到, 则可以看到 对象实例 未初始化的实例成员. 比如上面说的final int i == null的现象.

    上面这些可以参考下面的字节码:

    class Sup{}

    class Sub extends Sup{

    final int i;

    Sub(){i=1;}

    void test(){new Sub();}

    }

    字节码为:

    class Sup {

    Sup();

    0 aload_0 [this]

    1 invokespecial java.lang.Object() [8]

    4 return

    }

    class Sub extends Sup {

    final int i;

    Sub();

    0 aload_0 [this]

    1 invokespecial Sup() [10]

    4 aload_0 [this]

    5 iconst_1

    6 putfield Sub.i : int [12]

    9 return

    void test();

    0 new Sub [1]

    3 invokespecial Sub() [19]

    6 return

    }

    这里的Sub对象实例 在 Sub.test()的0行就已经 生成了, 也就是说这里this已经指向了 新生成对象实例的内存空间. 但是Sub对象实例的 final成员i, 是在Sub初始化函数的第6行才被赋值.

    再说一下 具体到 JCP 的代码, "注册listener监听器,或者start()新的线程,为什么会存在this指针逸出的风险"

    因为 内部类 隐式包含了 外部类的 this指针, 这个见下面的示例:

    class Outter1{

    private class Inner{}

    Outter1(){}

    }

    字节码:

    class Outter1 {

    Outter1();

    0 aload_0 [this]

    1 invokespecial java.lang.Object() [8]

    4 return

    }

    private class Outter1$Inner {

    final synthetic Outter1 this$0;

    private Outter1$Inner(Outter1 arg0);

    0 aload_0 [this]

    1 aload_1 [arg0]

    2 putfield Outter1$Inner.this$0 : Outter1 [10]

    5 aload_0 [this]

    6 invokespecial java.lang.Object() [12]

    9 return

    }

    重点是 内部类里 有这个:

    final synthetic Outter1 this$0;

    synthetic 表示这个是编译器 自己加上去的. 在内部类的初始化函数中此引用被 赋值.

    展开全文
  • 对象逸出:一种错误的发布,当一个对象还没有构造结束就已经提供给了外部代码一个对象引用即发布了该对象,此时叫做对象逸出,对象的逸出会破坏线程的安全性。我们最需要关注的就是对象逸出的问题,在不该发布该对象...

    对象发布(Publish)和逸出(Escape)

    对象发布:就是提供一个对象的引用给作用域之外的代码。比如return一个对象,或者作为参数传递到其他类的方法中。

    对象逸出:一种错误的发布,当一个对象还没有构造结束就已经提供给了外部代码一个对象引用即发布了该对象,此时叫做对象逸出,对象的逸出会破坏线程的安全性。

    我们最需要关注的就是对象逸出的问题,在不该发布该对象的地方就不要发布该对象,例如以下代码:

    class UnsafeStates{

    private String[] states = new String[]{"AK", "AL"};

    //states变量作用域是private而我们在getStates方法中却把它发布了,

    //这样就称为数组states逸出了它所在的作用域。

    public String[] getStates(){

    return states;

    }

    public static void main(String[] args) {

    UnsafeStates unsafeStates = new UnsafeStates();

    //此处我们通过getStates方法对private修饰的states做出更改。(对象逸出了)

    unsafePublish.getStates()[0] = "AA";

    }

    }

    我们再来看看更加隐秘的this逸出,那么什么是this逸出?观察以下代码:

    public class ThisEscape{

    private int value;

    public ThisEscape(EventSource source){

    source.registerListener{

    //当事件监听类注册完毕后,

    //实际上我们已经将EventListener匿名内部类发布出去了

    //而此时我们的一些初始化工作还没有完成

    //也就是一个类还没有构造完成已经将对象发布出去了

    new EventListener(){

    public void onEvent(Event e){

    doSomething(e);

    }

    }

    }

    //一些初始化工作

    value = 7;

    }

    public void doSomething(Event e){

    System.out.println(value);//this对象逸出,有可能在构造方法初始化的时候一些初始工作未完成而提前发布了对象从而导致对象逸出的问题

    }

    }

    安全的发布对象(单例模式的讲解)

    线程不安全的,在多线程环境下如果同时有多个线程通过getInstance创建SingletonExample1实例,有可能导致SingletonExample1实例的多次创建,影响程序逻辑。

    /**

    * 懒汉模式

    * 单例实例在第一次使用时进行创建

    */

    public class SingletonExample1 {

    // 私有构造函数

    private SingletonExample1() {

    }

    // 单例对象

    private static SingletonExample1 instance = null;

    // 静态的工厂方法

    public static SingletonExample1 getInstance() {

    if (instance == null) {

    instance = new SingletonExample1();

    }

    return instance;

    }

    }

    线程安全的,在SingletonExample2首次加载时创建对象。

    /**

    * 饿汉模式

    * 单例实例在类装载时进行创建

    */

    public class SingletonExample2 {

    // 私有构造函数

    private SingletonExample2() {

    }

    // 单例对象

    private static SingletonExample2 instance = new SingletonExample2();

    // 静态的工厂方法

    public static SingletonExample2 getInstance() {

    return instance;

    }

    }

    线程不安全的,双重检测机制一般不会发生线程安全问题,但是不可避免有可能会出现线程安全问题,比如发生了指令重排。

    /**

    * 懒汉模式 -》 双重同步锁单例模式

    * 单例实例在第一次使用时进行创建

    */

    public class SingletonExample4 {

    // 私有构造函数

    private SingletonExample4() {

    }

    // 1、memory = allocate() 分配对象的内存空间

    // 2、ctorInstance() 初始化对象

    // 3、instance = memory 设置instance指向刚分配的内存

    // JVM和cpu优化,发生了指令重排。有可能会导致还未初始化完成的对象已经被别的线程所使用。

    // 1、memory = allocate() 分配对象的内存空间

    // 3、instance = memory 设置instance指向刚分配的内存

    // 2、ctorInstance() 初始化对象

    // 单例对象

    private static SingletonExample4 instance = null;

    // 静态的工厂方法

    public static SingletonExample4 getInstance() {

    if (instance == null) { // 双重检测机制 // B

    synchronized (SingletonExample4.class) { // 同步锁

    if (instance == null) {

    instance = new SingletonExample4(); // A - 3

    }

    }

    }

    return instance;

    }

    }

    线程安全的,volatile会禁止指令重排

    /**

    * 懒汉模式 -》 双重同步锁单例模式

    * 单例实例在第一次使用时进行创建

    */

    @ThreadSafe

    public class SingletonExample5 {

    // 私有构造函数

    private SingletonExample5() {

    }

    // 1、memory = allocate() 分配对象的内存空间

    // 2、ctorInstance() 初始化对象

    // 3、instance = memory 设置instance指向刚分配的内存

    // 单例对象 volatile + 双重检测机制 -> 禁止指令重排

    private volatile static SingletonExample5 instance = null;

    // 静态的工厂方法

    public static SingletonExample5 getInstance() {

    if (instance == null) { // 双重检测机制 // B

    synchronized (SingletonExample5.class) { // 同步锁

    if (instance == null) {

    instance = new SingletonExample5(); // A - 3

    }

    }

    }

    return instance;

    }

    }

    线程安全的,但并不推荐,因为每次通过getInstance获取对象时都会加锁,而影响程序在高并发下的性能。

    // 私有构造函数

    private SingletonExample3() {

    }

    // 单例对象

    private static SingletonExample3 instance = null;

    // 静态的工厂方法

    public static synchronized SingletonExample3 getInstance() {

    if (instance == null) {

    instance = new SingletonExample3();

    }

    return instance;

    }

    线程安全并且是推荐的写法。

    /**

    * 枚举模式:最安全

    */

    public class SingletonExample7 {

    // 私有构造函数

    private SingletonExample7() {

    }

    public static SingletonExample7 getInstance() {

    return Singleton.INSTANCE.getInstance();

    }

    private enum Singleton {

    INSTANCE;

    private SingletonExample7 singleton;

    // JVM保证这个方法绝对只调用一次

    Singleton() {

    singleton = new SingletonExample7();

    }

    public SingletonExample7 getInstance() {

    return singleton;

    }

    }

    }

    线程安全的,推荐使用的一种创建单线程的方式

    /**

    * 懒汉式,静态内部类的方式

    */

    public class Singleton {

    private static class LazyHolder {

    private static final Singleton INSTANCE = new Singleton();

    }

    private Singleton() {

    }

    public static final Singleton getInstance() {

    return LazyHolder.INSTANCE;

    }

    }

    展开全文
  • “发布( Publish)”一个对象的意思是指,使对象能够在当前作用域之外的代码中使用。例如将一个指向该对象的引用保存到其他代码可以访问的地方,或者在某一个非私有的方法中返回该引用,或者将引用传递到其他类的方法...

    “发布( Publish)”一个对象的意思是指,使对象能够在当前作用域之外的代码中使用。例如将一个指向该对象的引用保存到其他代码可以访问的地方,或者在某一个非私有的方法中返回该引用,或者将引用传递到其他类的方法中。在许多情况中,我们要确保对象及其内部状态不被发布。而在某些情况下,我们又需要发布某个对象,但如果在发布时要确保线程安全性,则可能需要同步。发布内部状态可能会破坏封装性,并使得程序难以维持不变性条件。例如,如果在对象构造完成之前就发布该对象,就会破坏线程安全性。当某个不应该发布的对象被发布时,这种情况就被称为逸出( Escape)。现在,我们首先来看看一个对象是如何逸出的。

    发布对象的最简单方法是将对象的引用保存到一个公有的静态变量中,以便任何类和线程都能看见该对象,如下面的程序:

    2f398d7f991a24480ba5bc7ecf92c11c.png

    在 initialize方法中实例化一个新的 HashSet对象,并对象的引用保存到静态变量knownSecrets中以发布该对象。

    当发布某个对象时,可能会间接地发布其他对象。如果将一个Secret对象添加到集合中,那么同样会发布这个对象,因为任何代码都可以遍历这个集合,并获得对这个新Secret对象的引用。同样,如果从非私有方法中返回一个引用,那么同样会发布返回的对象。下面的程序UnsafeStates发布了本应为私有的状态数组。

    57c74f1a66edc4089d7d00bc7e37a2ab.png

    如果按照上述方式来发布 states,就会出现问题,因为任何调用者都能修改这个数组的内容。在这个示例中,数组 states已经逸出了它所在的作用域,因为这个本应是私有的变量已经被发布了。

    当发布一个对象时,在该对象的非私有域中引用的所有对象同样会被发布。一般来说,如果一个已经发布的对象能够通过非私有的变量引用和方法调用到达其他的对象,那么这些对象也都会被发布。

    假定有一个类C,对于C来说,“外部(Aien)方法”是指行为并不完全由C来规定的方法,包括其他类中定义的方法以及类C中可以被改写的方法(既不是私有[private]方法也不是终结[final]方法)。当把一个对象传递给某个外部方法时,就相当于发布了这个对象。你无法知道哪些代码会执行,也不知道在外部方法中究竟会发布这个对象,还是会保留对象的引用并在随后由另一个线程使用。

    无论其他的线程会对巳发布的引用执行何种操作,其实都不重要,因为误用该引用的风险始终存在。如果有人窃取了你的密码并发布到网络上,那么你的信息将“逸出”:无论是否有人会(或者尚未)恶意地使用这些个人信息,你的账户都已经不再安全了。发布一个引用同样会带来类似的风险。当某个对象逸出后,你必须假设有某个类或线程可能会误用该对象。这正是需要使用封装的最主要原因:封装能够使得对程序的正确性进行分析变得可能,并使得无意中破坏设计约束条件变得更难。

    最后一种发布对象或其内部状态的机制就是发布一个内部的类实例,如下面的程序:

    98d87fadf6d652fb93e079ca8ced850c.png

    当 ThisEscape发布 EventListener时,也隐含地发布了 ThisEscape实例本身,因为在这个内部类的实例中包含了对 ThisEscape实例的隐含引用。

    安全的对象构造过程

    在 ThisEscape中给出了逸出的一个特殊示例,即this引用在构造函数中逸出。当内部的EventListener实例发布时,在外部封装的 ThisEscape实例也逸出了。当且仅当对象的构造函数返回时,对象才处于可预测的和一致的状态。因此,当从对象的构造函数中发布对象时,只是发布了一个尚未构造完成的对象。即使发布对象的语句位于构造函数的最后一行也是如此。如果this引用在构造过程中逸出,那么这种对象就被认为是不正确构造。具体来说,只有当构造函数返回时,this引用才应该从线程中逸出。构造函数可以将this引用保存到某个地方,只要其他线程不会在构造函数完成之前使用它。文章最后的程序中就使用了这种技术。总之,不要在构造函数中使用this引用逸出。

    在构造过程中使this引用逸出的一个常见错误是,在构造函数中启动一个线程。当对象在其构造函数中创建一个线程时,无论是显式创建(通过将它传给构造函数)还是隐式创建(由于Thread或 Runnable是该对象的一个内部类),this引用都会被新创建的线程共享。在对象尚未完全构造之前,新的线程就可以看见它。在构造函数中创建线程并没有错误,但最好不要立即启动它,而是通过一个start或initialize方法来启动。在构造函数中调用一个可改写的实例方法时(既不是私有方法,也不是终结方法),同样会导致this引用在构造过程中逸出。

    如果想在构造函数中注册一个事件监听器或启动线程,那么可以使用一个私有的构造函数和一个公共的工厂方法(Factory Method),从而避免不正确的构造过程,如下面的程序:

    a9accb08fc2615da11b5fbb9e8a20a8d.png

    148f0ca1bbb5eea823dd5cc688551edb.png

    展开全文
  • 转自:http://blog.csdn.net/joker_zhou/article/details/7322801(1)发布:发布是指将一个对象,使其引用储存到一个其他代码可以访问到的地方,在一个非私有方法返回这个引用,也可以把它传递到其他对象中.a)发布最简单...

    转自:http://blog.csdn.net/joker_zhou/article/details/7322801

    (1)发布:发布是指将一个对象,使其引用储存到一个其他代码可以访问到的地方,在一个非私有方法返回这个引用,也可以把它传递到其他对象中.

    a)  发布最简单的就是将对象设置到公共静态域中

    b)  发布第二种简单的方式就是在一个公共方法内直接return 对象的引用

    第三种的发布就很隐秘了.就是讲自身的对象引用发布到另一个对象的引用中.

    可能在构造中出现如此这就是this例如:

    d55d21c956c4f30e25ad05f3c550ce06.gif

    这样 source就持有ThisEscape的内部类EvenListener()而Evenlistener可能会带出ThisEscape中的保护数据引用,可以使用工厂和私有构造防止发布逸出,这样ThisEscape对象就可以构造完成然后使用ThisEscape.registerListener(source)放入监听器.最好不要在构造内注册监听器或者启动线程.而在工厂内初始化这些事情.

    class A1{

    public EventListener l;

    }

    public class MyThread{

    EventListener l=null;

    private MyThread(){

    l=new EventListener() {

    };

    }

    public static MyThread newInstance(A1 a){

    MyThread my=new MyThread();

    a.l=my.l;

    return my;

    }

    }

    其实A1中的l我没有封装.应该加以封装.我只是掩饰下如何注册

    (2)逸出:如果对象在没有构造完成前就将其发布,这就是逸出.在构造中启动线程,会使未构造完成的对象发布出去,造成逸出.

    (3)如何安全的发布:

    a)  通常发布线程与消费线程需要同步化.

    b)  通过静态初始化初始对象的引用.

    c)  引用储存到volatile或者AtomicReference

    d)  引用储存到正确创建对象的final域中

    (4)如果需要发布一个非线程安全的状态集(就是包含基本类型的对象),可以先复制对象,然后发布复制后的对象.这样避免外来客户修改非线程安全的类.造成同步错误.这前提是不需要此集合状态在多个线程间一致.

    (5)发布一个线程安全的类,并不需要复制-发布.

    (6)可以安全的发布一个线程安全的类并且发布时不需要有任何我约束行为

    时间: 09-03

    展开全文
  • this引用逸出

    2019-02-01 16:51:02
    逸出:某个不应被发布的对象被发布 ...《Java并发编程实战》3-2节中提到this隐式逸出,并给出了示例代码: public class ThisEscape { public ThisEscape(EventSource source) { source.registerLi...
  • 标签:发布(Publish)和逸出(Escape)这两个概念倒是第一次听说,不过它在实际当中却十分...如果一个类还没有构造结束就已经提供给了外部代码一个对象引用即发布了该对象,此时叫做对象逸出,对象的逸出会破坏线程的安...
  • 这是危及到线程安全的,因为其他线程有可能通过这个逸出的引用访问到“初始化了一半”的对象(partially-constructed object)。这样就会出现某些线程中看到该对象的状态是没初始化完的状态,而在另外一些线程看到的却...
  • 文章目录解决private对象逸出--返回副本 解决private对象逸出–返回副本 如下的代码, 使用的是返回对象的副本的方式, 来解决private对象逸出 package com.thread.background; import java.util.HashMap; import ...
  • 最近在看《Java 并发编程实践》看到3.2章里面的关于发布和逸出的部分,写一下心得,算是mark一下,主要是构造过程中this引用的逸出。 书上面给出了一个构造过程中this逸出的例子: public classThisEscape {...
  • 最近在看《Java 并发编程实践》看到3.2章里面的关于发布和逸出的部分,写一下心得,算是mark一下,主要是构造过程中this引用的逸出。书上面给出了一个构造过程中this逸出的例子:public class ThisEscape {public ...
  • 对象逸出: 对象发布到了不该发布的地方. 如下的情况都是逸出 如下的代码演示了发布逸出的情况 ,定义了一个private对象, 并且用一个public的方法getStates去返回该对象. 在ReleaseEscapeDemo的构造方法中, 给这个map...
  • Java对象的共享

    2021-03-06 13:57:24
    摘要:Java内存可见性、对象的发布和逸以及不变性Abstract:Visibility、publish and escape、immutable。可见性可见性是指某个线程对变量写入的值,其他线程是否总能够正确的读取。通常,为了确保多线程之间对内存...
  • 1.什么是this对象this就是该对象实例本身2.何为发布和逸出发布,就是把对象暴露给他人,这就是为什么会需要用到封装,不能...3.什么是构造过程中this引用逸出public class Test {private boolean isIt;public Test...
  • 安全发布与逸出 1.对象发布的几种形式 对象发布是指使对象能够被其作用域之外的线程访问。常见的对象发布形式包括以下几种。 将对象引用存储到public变量中。例如: public Map<String, Integer> ...
  • java并发编程实战》中介绍了“发布与逸出”,首先介绍下发布,然后重点介绍this引用逸出。一、发布“发布”一个对象是指,使对象能够在当前作用域之外的代码中使用。例如:①将一个指向该对象的引用保存到其他代码...
  • 最近在看《Java并发编程实战》这本书的“3.2 发布与逸出”章节时,对“this”引用在对象构造方法中逸出问题的解决方法有点小疑惑。书上是这么说的:如果想在构造函数中注册一个事件监听器或启动线程,那么可以使用一...
  • ​​​​​​​​​​​​​​​​​​​​​​​​​​​​这是《Java并发编程实战》3.2发布与逸出一节中的示例代码。我无法理解,this是怎么逸出的。 //隐式地使this引用逸出(不要这么做) public class ...
  • 四、Java共享对象

    2018-12-11 16:07:43
    学习内容 一、共享引发的问题  1.1、可见性  1.2、原子性  1.3、解决方案 ...二、发布与逸出  2.2、不安全的发布  2.3、安全发布  不可变对象、事实不可变对象、可变对象  安全发布的常用模式 ...
  • 概念发布(publishing): ... 一个对象在尚未准备好时就将它发布,这种情况称作逸出逸出的方式上边关于逸出的概念讲述的很是模糊,下面列举几个逸出的示例。第一,通过静态变量引用逸出public st
  • Java发布和逸

    2019-05-22 10:32:50
    发布 发布(publish)一个对象的意思是指,使对象能够在当前作用域之外的代码中使用。 可以通过公有静态变量,非私有方法,构造方法内隐含引用 三种方式。 public static Set<String>...逸出 当某...
  • 详谈java构造方法和权限

    千次阅读 2017-10-03 19:25:42
    1,java构造方法1)什么是构造方法? 名字和类名完全一样,没有返回值(可理解为返回一个对象), 用new来调,调用的结果是在堆内存中创建一个对象空间且返回它的堆内存首地址 public class MyDate_1 { private ...
  • 逸出:如果再对象构造完全之前就发布该对象,就会破坏线程安全性。当某个不应该发布的对象被发布时,这种情况就被成为逸出(Escape) 发布比较好理解,像单例模式,还有做初始化操作的时候,都是这样。比如像书中...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,327
精华内容 930
关键字:

java构造对象逸出

java 订阅