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

    对象发布(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)和逸出(Escape)这两个概念倒是第一次听说,不过它在实际当中却十分...如果一个类还没有构造结束就已经提供给了外部代码一个对象引用即发布了该对象,此时叫做对象逸出,对象的逸出会破坏线程的安全性。...

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

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

    什么是逸出?如果一个类还没有构造结束就已经提供给了外部代码一个对象引用即发布了该对象,此时叫做对象逸出,对象的逸出会破坏线程的安全性。

    概念我们知道了,可我们要关注什么地方呢?我们要关注的时候就是逸出问题,在不该发布该对象的地方就不要发布该对象,例如以下代码:

    0818b9ca8b590ca3270a3433284dd417.png

    1 classUnsafeStates{2 private String[] states = new String[]{"AK", "AL"};3

    4 publicString[] getStates(){5 returnstates;6 }7 }

    0818b9ca8b590ca3270a3433284dd417.png

    states变量作用域是private而我们在getStates方法中却把它发布了,这样就称为数组states逸出了它所在的作用域。

    然而更加隐蔽和需要我们注意的是this逸出,这个问题要引起重点关注。什么是this逸出?观察以下代码:

    0818b9ca8b590ca3270a3433284dd417.png

    1 public classThisEscape{2 private intvalue;3 publicThisEscape(EventSource source){4 source.registerListener{5 newEventListener(){6 public voidonEvent(Event e){7 doSomething(e);8 }9 }10 }11 //一些初始化工作

    12 value = 7;13 }14

    15 public voiddoSomething(Event e){16 System.out.println(value);17 }18

    19 }

    0818b9ca8b590ca3270a3433284dd417.png

    在构造方法中我们定义了一个匿名内部类,匿名内部类是一个事件监听类,当事件监听类注册完毕后,实际上我们已经将EventListener匿名内部类发布出去了,而此时我们实际上已经携带了this逸出,重点在于这个时候我们还有一些初始化工作没有做完(代码11行之后),这也就是上面所说的,一个类还没有构造结束我们已经将发布了。那怎么来避免this逸出呢?既然我们没有构造完构造函数,那我们就将构造函数构造完嘛,将构造函数定义为private作用域。如以下代码所示:

    0818b9ca8b590ca3270a3433284dd417.png

    1 public classSafeListener{2 private finalEventListener listener;3

    4 privatesafeListener(){5 listener = newEventListener(){6 public voidonEvent(Event e){7 doSomething(e);8 }9 }10 }11

    12 public staticSafeListener newInstance(EventSource source){13 SafeListener safeListener = newSafeListener();14 safeListener.registerListener(safeListener.listener);15

    16 returnsafeListener;17 }18 }

    0818b9ca8b590ca3270a3433284dd417.png

    我们首先将构造函数设定为private,其次我们在构造函数未完成时不将对象进行发布,而是使用工厂方法,在工厂方法newInstance中待构造函数执行完毕后再将对象进行发布(代码中即为registenerListener注册监听)。这实际上就是修改为了构造完毕->发布对象的串行执行模式,而不是之前的异步模式,这样就不会给我们带来线程安全性的问题。

    展开全文
  • 为什么说 "构造函数中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;

    }

    }

    展开全文
  • 对象逸出: 对象发布到了不该发布的地方. 如下的情况都是逸出 如下的代码演示了发布逸出的情况 ,定义了一个private对象, 并且用一个public的方法getStates去返回该对象. 在ReleaseEscapeDemo的构造方法中, 给这个map...
  • import java.io.IOException; public class Demo { public Demo(T1 t1){ ...// 将this在构造期间就逸出,这样逸出对象被认为是“没有正确构建的(not properly constructed)” try { Thread...
  • 文章目录工厂模式 ...构造方法使用private , 这样其他的对象不能调用构造方法. 创建此工厂方法, 完成了初始化之后, 再进行注册 main方法修改如下 : 完整的代码如下: package com.thread.background; /** *
  • 最近在看《Java并发编程实战》这本书的“3.2 发布与逸出”章节时,对“this”引用在对象构造方法中逸出问题的解决方法有点小疑惑。书上是这么说的:如果想在构造函数中注册一个事件监听器或启动线程,那么可以使用一...
  • 对象的公布与逸出 “公布(Publish)“一个对象是指使对象可以在当前作用域之外的代码中使用。可以通过 公有静态变量。非私有方法。构造方法内隐含引用三种方式。 假设对象构造完毕之前就公布该对象,就会破坏...
  • 这是危及到线程安全的,因为其他线程有可能通过这个逸出的引用访问到“初始化了一半”的对象(partially-constructed object)。这样就会出现某些线程中看到该对象的状态是没初始化完的状态,而在另外一些线程看到的却...
  • 当某个不应该发布的对象被发布时,这种情况就被称为逸出(Escape)。 下面我们首先来看看一个对象是如何逸出的。 发布对象最简单的方法便是将对象的引用保存到一个共有的静态变量中,以便任何类和线程都能看见对象...
  • 对于final 域,编译器和处理器要遵守两个重排序规则:在构造函数内对一个 final 域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。初次读一个包含 final 域的对象的引用,与...
  • 对象逸出

    2013-03-05 11:20:39
    不要在构造器中将this对象发布出去 [code="java"] public class Escape { int i; public Escape() { // TODO Auto-generated constructor stub new Thread (){ public void run() { ...
  • 发布(Publish)和逸出(Escape)这两个概念倒是第一次听说,不过它在实际当中却十分...如果一个类还没有构造结束就已经提供给了外部代码一个对象引用即发布了该对象,此时叫做对象逸出,对象的逸出会破坏线程的安全性。...
  • 发布(Publish)和逸出(Escape)...如果一个类还没有构造结束就已经提供给了外部代码一个对象引用即发布了该对象,此时叫做对象逸出,对象的逸出会破坏线程的安全性。 class UnsafeStates{ private String[] state...
  • java发布_JAVA发布对象

    2021-03-05 14:32:51
    定义使一个对象能够被当前范围之外的代码使用对象逸出一种错误的发布,当一个对象还没构造完成时就被其他线程所见安全地发布对象在静态初始化函数中初始化一个对象引用将对象的引用保存到volatile类型或者...
  • 逸出(escape) : 指一个对象尚未构造完成已经发布,该对象引用被发布。 二、逸出方式1、共有静态变量引用方式逸出 public static Set knownSecrets ; public void initialize(){ knownSecrets = new Hash
  • 在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序2.初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序有了这个限制...
  • 发布(Publish)和逸出(Escape)是Java并发编程中... “逸出”,是指如果一个类还没有构造结束就已经提供给了外部代码一个对象引用即发布了该对象,此时叫做对象逸出,对象的逸出会破坏线程的安全性。 下面代码...
  • 发布(Publish)和逸出(Escape)这两个概念倒是第一次听说,不过它在实际当中却十分常见,这和Java...如果一个类还没有构造结束就已经提供给了外部代码一个对象引用即发布了该对象,此时叫做对象逸出,对象的逸出会...
  • 发布逸出 (1)发布:发布是指将一个对象,使其引用储存到一个其他代码可以访问到的地方,在一个非私有方法返回这个引用,也可以把它传递到其他对象中. a) 发布最简单的就是将对象设置到公共静态域中 b) 发布第二...
  • java中的final对重排序的限制与逸出

    千次阅读 2018-06-17 15:52:34
    在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序2.初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序 有了这个...
  • 当某个不应该被发布的对象被发布了则成为逸出(比如再对象构造之前就发布该对象)。 注意发布一个对象,它的非私有的属性也同样会被发布。 比如一种通过内部类发布对象,这时就会将本类的this引用发布出去(内部类...
  • 并发编程实践中,this引用逃逸("this"escape)是指对象还没有构造完成,它的...这是危及到线程安全的,因为其他线程有可能通过这个逸出的引用访问到“初始化了一半”的对象(partially-constructed object)。这样

空空如也

空空如也

1 2 3 4
收藏数 79
精华内容 31
关键字:

java构造对象逸出

java 订阅