博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java并发编程(六)发布与逸出
阅读量:7079 次
发布时间:2019-06-28

本文共 2281 字,大约阅读时间需要 7 分钟。

 

"发布(Publish)"一个对象的意思指,使对象能够在作用域之外的代码中使用。

 

例如:

将一个指向该对象的引用保存到其他代码可以访问的地方

在一个非私有的方法中返回该引用

将引用传递到其他类的方法中

 

有时候要确保对象及其内部状态不被发布,但是某些情况下又需要发布。

 

如果在发布时要确保线程安全性,则可能需要同步。

 

发布内部状态可能会破坏封装性,并使得程序难以维持不变性条件。

 

比如在对象构造完成之前就发布该对象,就会破坏线程安全性。

 

 

当某个不应该被发布的对象被发布时,这种情况就叫做"逸出"。

 

逸出的例子:

 

public class PublishDemo {    public static Set
intSet; public void initialize() { intSet = new HashSet<>(); }}

在发布某个对象的时候,可能会间接发布其他对象。比如上面代码中的不仅Set这个集合会被发布出去,Set中的Integer对象也会被发布出去。

 

public class UnsafeStates {    private String[] states = new String[] {"AK", "47"};    public String[] getStates() {        return states;    }}

 

简而言之,就是本来一个人可以经手处理的事情,现在搞得n多个人都可以插上一手了,你说安全不安全。当某个对象逸出之后,必须要假设某个类或者线程会误用该对象。这正是需要封装的原因:封装能够使得对程序的正确性进行分析变得可能,并使得无意中破坏设计约束条件变得更难。

 

还有一种发布对象或其内部状态的机制就是发布一个内部的类实例。

public class PublishInnerClass {    public PublishInnerClass(EventSource source) {        source.registerListener(            new EventListener() {                public void onEvent(Event e) {                    System.out.println("heheda");                }            }        );    }    class EventSource {        public void registerListener(EventListener eventListener) {            // 暴露出去了        }    }}

不要在构造过程中使this逸出。

 

在构造过程中使this引用逸出的一个常见错误是,在构造函数中启动一个线程。当对象在其构造函数中创建一个线程时,无论是显式创建(通过将它传递给构造函数)还是隐式创建(由于Thread或Runnable是该对象的一个内部类),this引用都会被新创建的线程共享。

 

在对象尚未完全构造之前,新的线程就可以看见她。在构造函数中创建线程并没有错误,但最好不要立即启动它,而是通过一个start或initialize方法启动。在构造函数中调用一个可改写的实例方法时(既不是私有方法,也不是终结方法),同样会导致this引用在构造过程中逸出。

 

新技能

 

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

 

public class SafeListener {    private final EventListener listener;    private SafeListener() {        listener = new EventListener() {            public void onEvent(Event e) {                System.out.println("呵呵哒");            }        };    }    class EventSource {        public void registerListener(EventListener listener) {        }    }    public static SafeListener newInstance(EventSource source) {        SafeListener safe = new SafeListener();        source.registerListener(safe.listener);        return safe;    }}

 

结论一:当注册的事件监听器监听到某一个事件发生时,会启用一个新的线程去执行相关的事情。(这个我们在代码中手动启动了一个线程来模拟,在没有监听器注册的时候,启动的线程会自动的阻塞!)

结论二:在外部内中初始化一个内部类的对象时,此内部类的对象保留了一个外部内对象的一个引用。这是java自带的一个机制,这在java中这种机制叫做"synthetic"。通过断点调试我们可以清晰的看到这种实现。

转载地址:http://qjcml.baihongyu.com/

你可能感兴趣的文章
浏览器加载与渲染
查看>>
HTTP常见错误返回代码
查看>>
安装homeassistant+python3.6
查看>>
老李分享:JDK,JRE,JVM区别与联系 1
查看>>
CentOS 7 上systemctl 的用法
查看>>
Android Design框架
查看>>
[Linux] 在 Linux CLI 使用 ssh-keygen 生成 RSA 密钥
查看>>
在 Ubuntu 16.04 Server 上安装 Zabbix
查看>>
Netgear wndr3700v2 路由器刷OpenWrt打造全能服务器(九)ftp服务
查看>>
oracle 存储过程的基本语法
查看>>
程序员应该遵守的编程原则
查看>>
各操作系统配置NTP
查看>>
使用mysql索引技巧及注意事项
查看>>
按照发起的方式,DDoS可以简单分为三类
查看>>
2月新书,送出一本你爱的!
查看>>
2018LinuxCon,开源界的大咖们来了,赶紧行动!
查看>>
10月24日程序员关爱日
查看>>
python函数定义
查看>>
服务器的安装
查看>>
如何优雅的处理异常(java)?
查看>>