Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
私有构造函数如何在Java中实现发布安全_Java_Multithreading_Constructor_Thread Safety_Safe Publication - Fatal编程技术网

私有构造函数如何在Java中实现发布安全

私有构造函数如何在Java中实现发布安全,java,multithreading,constructor,thread-safety,safe-publication,Java,Multithreading,Constructor,Thread Safety,Safe Publication,在经典的Java并发实践中,Brian Goetz使用以下代码片段演示如何使用私有构造函数和工厂方法安全地发布对象: public class SafeListener { private final EventListener listener; private SafeListener() { listener = new EventListener() { public void onEvent(Event e) {

在经典的Java并发实践中,Brian Goetz使用以下代码片段演示如何使用私有构造函数和工厂方法安全地发布对象:

public class SafeListener {
    private final EventListener listener;

    private SafeListener() {
        listener = new EventListener() {
            public void onEvent(Event e) {
                doSomething(e);
            }
        };
    }

    public static SafeListener newInstance(EventSource source) {
        SafeListener safe = new SafeListener();
        source.registerListener(safe.listener);
        return safe;
    }
}
我还不知道这段代码是如何通过私有构造函数实现安全发布的


我知道私有构造函数用于防止对象外部的实例化,但这如何应用于线程而不是对象?一个线程不一定是一个对象,我看不出是什么阻止另一个线程在构造函数完成执行之前获取对safe的引用。

这里的要点是防止在构造函数完成之前执行此操作。为此,构造函数被设置为私有,并提供工厂方法,在对象的构造函数完成后,该方法负责在外部代码中注册侦听器


线程安全API的一个例子。

这里的要点是防止在构造函数完成之前执行此操作。为此,构造函数被设置为私有,并提供工厂方法,在对象的构造函数完成后,该方法负责在外部代码中注册侦听器

线程安全API的一个示例

这是如何应用于线程而不是对象的?线程不一定是对象,我看不出是什么阻止另一个线程在构造函数完成执行之前获取对safe的引用

当然,线程始终是java.lang.Thread意义上的对象。该模式不应应用于线程本身。相反,它可以应用于新线程必须与对象的构造一起启动的情况。在构造函数中启动线程可以使对未完全构造的对象的引用转义。但是,使用此模式,新构造的实例将被困在newInstance方法中,直到其构造完全完成

或者这样说:我无法想象另一个线程如何在其构造完成之前获取对安全实例的引用。也许你可以举一个例子,说明你认为这是如何发生的

这是如何应用于线程而不是对象的?线程不一定是对象,我看不出是什么阻止另一个线程在构造函数完成执行之前获取对safe的引用

当然,线程始终是java.lang.Thread意义上的对象。该模式不应应用于线程本身。相反,它可以应用于新线程必须与对象的构造一起启动的情况。在构造函数中启动线程可以使对未完全构造的对象的引用转义。但是,使用此模式,新构造的实例将被困在newInstance方法中,直到其构造完全完成


或者这样说:我无法想象另一个线程如何在其构造完成之前获取对安全实例的引用。也许您可以给出一个示例,说明您认为这可能是如何发生的。

构造函数的私有属性与线程安全无关。这是最终现场发布担保的一个示例。为了使其工作,在构造函数期间不能转义final字段的this实例,因此工厂方法首先构造holder实例,然后注册侦听器。工厂模式的应用程序自然会有私有构造函数和公共工厂方法,但这对于实现线程安全性并不重要

这一切都是关于JIT和热点优化器在执行优化时如何处理代码。他们知道什么是final或volatile字段,什么是构造函数。他们将遵守此类结构优化程度的限制。最重要的是,它们确保对存储在final字段中的对象的所有写入以及对final字段本身的写入都发生在构造函数结束之前,因此final字段存储发生在您的示例中的registerListener调用生效之前。因此,在正确初始化之前,其他线程无法看到侦听器引用


请注意,这是您很少应该依赖的。在您的示例中,如果EventSource的方法registerListener不是线程安全的,那么仍然可能发生非常糟糕的事情。另一方面,如果它是线程安全的,那么它的线程安全性也将应用于注册之前构造的侦听器,因此不需要最终的字段保证。

构造函数的私有属性与线程安全无关。这是最终现场发布担保的一个示例。为了让它工作,在构造函数期间,final字段的this实例不能逃逸,因此工厂方法负责构造ho 先注册一个侦听器实例,然后注册侦听器。工厂模式的应用程序自然会有私有构造函数和公共工厂方法,但这对于实现线程安全性并不重要

这一切都是关于JIT和热点优化器在执行优化时如何处理代码。他们知道什么是final或volatile字段,什么是构造函数。他们将遵守此类结构优化程度的限制。最重要的是,它们确保对存储在final字段中的对象的所有写入以及对final字段本身的写入都发生在构造函数结束之前,因此final字段存储发生在您的示例中的registerListener调用生效之前。因此,在正确初始化之前,其他线程无法看到侦听器引用


请注意,这是您很少应该依赖的。在您的示例中,如果EventSource的方法registerListener不是线程安全的,那么仍然可能发生非常糟糕的事情。另一方面,如果它是线程安全的,那么它的线程安全性也将适用于注册之前构造的侦听器,因此不需要最终的字段保证。

这里给出的答案讨论了您的部分问题。构造函数实际上是不同步的,可能会执行您描述的行为。解决方案可能是将初始化实例放在一个同步方法中,但我可能弄错了。这里给出的答案讨论了你问题的一部分。构造函数实际上是不同步的,可能会执行您描述的行为。解决方案可能是将初始化实例放在同步方法中,但我可能弄错了。因此,您的意思是,如果我执行myObject=newInstanceevent,则仅在对象完全构造之后才分配对象。然而,当它被构造时,另一个线程是否能够以某种方式介入并控制safe?我相信它可以,但因为SafeListener safe正在调用线程的堆栈上执行,所以它是本地的,直到该线程返回它,它最终无法执行。无论如何,我有点困惑…@Konos5:从创建线程的角度来看,所有事情都是按正确的顺序发生的,因此在注册侦听器之前,本地构造的对象不会被其他人看到。但是,由于某些优化,其他线程可能会以不同的顺序感知效果。最终的字段安全发布阻止了某些优化。考虑到在构建过程中,另一个线程是否能够以某种方式介入并控制安全,答案是:否。当另一个线程进入此方法时,它将无法访问相同的安全对象。在该方法中,安全对象仅存在于堆栈上。每个线程都有自己的堆栈。因此,当另一个线程进入此方法时,它将只能访问自己的新安全实例,而不能访问第一个线程创建的实例。对,本地对象不需要线程安全构造。重要的一点是,遍历注册到EventSource的侦听器列表的线程可能会看到该实例。如果没有线程安全构造,他们可能会看到它,但不会看到其实例字段的正确值。正如我在答覆中所说,,如果事件源/侦听器注册是线程安全的,则不需要特殊的侦听器工厂。因此,据我所知,这一切归结为两个主要威胁:1“横冲直撞”线程,它们试图控制“安全”,但无法导致在方法内创建安全,这意味着它是线程受限的,因此每个线程都有自己的安全性自己的副本。2个“讨厌的迭代器”线程正在查看声明最终字段的此实例的侦听器,但它们再次失败,因为safe在其构造函数完成之前不会针对侦听器注册,因此它们将无法看到它。因此,您的意思是,如果我执行myObject=newInstanceevent,则仅在它是完全建成的。然而,当它被构造时,另一个线程是否能够以某种方式介入并控制safe?我相信它可以,但因为SafeListener safe正在调用线程的堆栈上执行,所以它是本地的,直到该线程返回它,它最终无法执行。无论如何,我有点困惑…@Konos5:从创建线程的角度来看,所有事情都是按正确的顺序发生的,因此在注册侦听器之前,本地构造的对象不会被其他人看到。但是,由于某些优化,其他线程可能会以不同的顺序感知效果。最终的字段安全发布阻止了某些优化。考虑到在构建过程中,另一个线程是否能够以某种方式介入并控制安全,答案是:否。当另一个线程进入此方法时,它将无法访问相同的安全对象。在该方法中,安全对象仅存在于堆栈上。每个线程都有自己的堆栈。因此,当另一个线程进入此方法时,它将
只能访问它自己的新安全实例,但不能访问第一个线程创建的实例。对,本地对象不需要线程安全构造。重要的一点是,遍历注册到EventSource的侦听器列表的线程可能会看到该实例。如果没有线程安全构造,他们可能会看到它,但不会看到其实例字段的正确值。正如我在答覆中所说,,如果事件源/侦听器注册是线程安全的,则不需要特殊的侦听器工厂。因此,据我所知,这一切归结为两个主要威胁:1“横冲直撞”线程,它们试图控制“安全”,但无法导致在方法内创建安全,这意味着它是线程受限的,因此每个线程都有自己的安全性自己的副本。2个“讨厌的迭代器”线程正在查看声明最终字段的此实例的侦听器,但它们再次失败,因为safe在其构造函数完成之前不会针对侦听器注册,因此它们将无法看到它。