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

例如:

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

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

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

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

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

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

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

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

逸出的例子:

public class PublishDemo {
public static Set<Integer> 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"。通过断点调试我们可以清晰的看到这种实现。

Java并发编程(六)发布与逸出的更多相关文章

  1. 【Java并发编程六】线程池

    一.概述 在执行并发任务时,我们可以把任务传递给一个线程池,来替代为每个并发执行的任务都启动一个新的线程,只要池里有空闲的线程,任务就会分配一个线程执行.在线程池的内部,任务被插入一个阻塞队列(Blo ...

  2. Java并发编程 (六) 线程安全策略

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 一.不可变对象-1 有一种安全的发布对象,即不可变对象. 1.不可变对象需要满足的条件 ① 对象创建以后 ...

  3. Java并发编程(五):Java线程安全性中的对象发布和逸出

    发布(Publish)和逸出(Escape)这两个概念倒是第一次听说,不过它在实际当中却十分常见,这和Java并发编程的线程安全性就很大的关系. 什么是发布?简单来说就是提供一个对象的引用给作用域之外 ...

  4. Java并发编程(六):Java里实现对象安全发布的四种方式

    接上篇,首先要了解什么是对象的发布与逸出? Java里安全发布对象的四种方法1.单例(注意懒汉和饿汉的区别)2.静态属性,注意类里的静态域和静态代码块的顺序有要求3.枚举4.final

  5. Java线程安全性中的对象发布和逸出

    发布(Publish)和逸出(Escape)这两个概念倒是第一次听说,不过它在实际当中却十分常见,这和Java并发编程的线程安全性就很大的关系. 什么是发布?简单来说就是提供一个对象的引用给作用域之外 ...

  6. 《java并发编程实战》读书笔记2--对象的共享,可见性,安全发布,线程封闭,不变性

    这章的主要内容是:如何共享和发布对象,从而使它们能够安全地由多个线程同时访问. 内存的可见性 确保当一个线程修改了对象状态后,其他线程能够看到发生的状态变化. 上面的程序中NoVisibility可能 ...

  7. java并发编程笔记(四)——安全发布对象

    java并发编程笔记(四)--安全发布对象 发布对象 使一个对象能够被当前范围之外的代码所使用 对象逸出 一种错误的发布.当一个对象还没构造完成时,就使它被其他线程所见 不安全的发布对象 某一个类的构 ...

  8. java 发布和逸出

    [转载]:http://www.2cto.com/kf/201310/247738.html 前言 多线程并发环境下,线程安全极为重要.往往一些问题的发生都是由于不正确的发布了对象造成了对象逸出而引起 ...

  9. Java并发编程(六)volatile关键字解析

    由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我们先来了解一下与内存模型相关的概念和知识. 一.内存模型的相关概念 Java内存模型规定所有的变量都是存在 ...

  10. Java并发编程的艺术(六)——线程间的通信

    多条线程之间有时需要数据交互,下面介绍五种线程间数据交互的方式,他们的使用场景各有不同. 1. volatile.synchronized关键字 PS:关于volatile的详细介绍请移步至:Java ...

随机推荐

  1. 输入参数之POJO包装类

    1,包装类:需要实现序列化 package com.songyan.pojo; import java.io.Serializable; public class QueryVo implements ...

  2. oracle表空间操作语句

    1.查看所有表空间及表空间大小: select tablespace_name ,sum(bytes) / 1024 / 1024 as MB from dba_data_files group by ...

  3. grub

    root (hd0,0)    kernel /vmlinuz-2.6.32-573.8.1.el6.i686 ro root=/dev/mapper/VolGroup-lv_root nomodes ...

  4. ExtentReports 结合 TestNg 生成自动化 html 报告 (支持多 suite)

    转载:https://testerhome.com/topics/8134 重要说明:报告监听器源码修复一些bug,不再此处更新代码,最新代码可以到github查看最新报告监听器源码 前几天分享了ht ...

  5. AutoConfig工具使用指南

    转载:http://blog.csdn.net/fighterandknight/article/details/70245905 13.1. 需求分析 13.1.1. 解决方案 13.2. Auto ...

  6. java基础知识汇总4

    三.集合(collection.set.list.map) 一.定义: 集合是Java里面最经常使用的,也是最重要的一部分.可以用好集合和理解好集合对于做Java程序的开发拥有无比的优点. 容器:用来 ...

  7. 基于.NET的轻量级微信SDK

    一.前言 特别不喜欢麻烦的一个人,最近碰到了微信开发.下载下来了一些其他人写的微信开发“框架”,但是被恶心到了,实现的太臃肿啦. 最不喜欢的就是把微信返回的xml消息在组装成实体类,所以会比较臃肿,现 ...

  8. MPTCP 源码分析(二) 建立子路径

    简述      MPTCP在进行三次握手之后,客户端和服务端会进行地址信息的交换,让对方知道彼此未用的地址信息. 当客户端知道服务端的地址后就可以建立其他子路径.三次握手和建立子路径的过程如图1:   ...

  9. python——异常except语句用法与引发异常

    except: #捕获所有异常 except: <异常名>: #捕获指定异常 except:<异常名1,异常名2):捕获异常1或者异常2 except:<异常名>,< ...

  10. jQuery (样式篇)

    1.$(document).ready 的作用是等页面的文档(document)中的节点都加载完毕后,再执行后续的代码,因为我们在执行代码的时候,可能会依赖页面的某一个元素,我们要确保这个元素真正的的 ...