Java并发编程(六)发布与逸出
"发布(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并发编程(六)发布与逸出的更多相关文章
- 【Java并发编程六】线程池
一.概述 在执行并发任务时,我们可以把任务传递给一个线程池,来替代为每个并发执行的任务都启动一个新的线程,只要池里有空闲的线程,任务就会分配一个线程执行.在线程池的内部,任务被插入一个阻塞队列(Blo ...
- Java并发编程 (六) 线程安全策略
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 一.不可变对象-1 有一种安全的发布对象,即不可变对象. 1.不可变对象需要满足的条件 ① 对象创建以后 ...
- Java并发编程(五):Java线程安全性中的对象发布和逸出
发布(Publish)和逸出(Escape)这两个概念倒是第一次听说,不过它在实际当中却十分常见,这和Java并发编程的线程安全性就很大的关系. 什么是发布?简单来说就是提供一个对象的引用给作用域之外 ...
- Java并发编程(六):Java里实现对象安全发布的四种方式
接上篇,首先要了解什么是对象的发布与逸出? Java里安全发布对象的四种方法1.单例(注意懒汉和饿汉的区别)2.静态属性,注意类里的静态域和静态代码块的顺序有要求3.枚举4.final
- Java线程安全性中的对象发布和逸出
发布(Publish)和逸出(Escape)这两个概念倒是第一次听说,不过它在实际当中却十分常见,这和Java并发编程的线程安全性就很大的关系. 什么是发布?简单来说就是提供一个对象的引用给作用域之外 ...
- 《java并发编程实战》读书笔记2--对象的共享,可见性,安全发布,线程封闭,不变性
这章的主要内容是:如何共享和发布对象,从而使它们能够安全地由多个线程同时访问. 内存的可见性 确保当一个线程修改了对象状态后,其他线程能够看到发生的状态变化. 上面的程序中NoVisibility可能 ...
- java并发编程笔记(四)——安全发布对象
java并发编程笔记(四)--安全发布对象 发布对象 使一个对象能够被当前范围之外的代码所使用 对象逸出 一种错误的发布.当一个对象还没构造完成时,就使它被其他线程所见 不安全的发布对象 某一个类的构 ...
- java 发布和逸出
[转载]:http://www.2cto.com/kf/201310/247738.html 前言 多线程并发环境下,线程安全极为重要.往往一些问题的发生都是由于不正确的发布了对象造成了对象逸出而引起 ...
- Java并发编程(六)volatile关键字解析
由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我们先来了解一下与内存模型相关的概念和知识. 一.内存模型的相关概念 Java内存模型规定所有的变量都是存在 ...
- Java并发编程的艺术(六)——线程间的通信
多条线程之间有时需要数据交互,下面介绍五种线程间数据交互的方式,他们的使用场景各有不同. 1. volatile.synchronized关键字 PS:关于volatile的详细介绍请移步至:Java ...
随机推荐
- url参数的获取
方法 function getQueryString(name) { var reg = new RegExp("(^|&)" + name + "=([^&am ...
- mac 下安装 mysql (蛋疼)
mac先安装mydql步骤 1.首先从官网上下载 mac版本的mysql:我的是mysql-5.7.20-macos10.12-x86_64.dmg 2.正常安装: 3.在系统偏好设置的最底下.1:是 ...
- Coincidence (动态规划求最长公共子序列)(王道)
题目描述: Find a longest common subsequence of two strings. 输入: First and second line of each input case ...
- Spark1.0.0 编程模型
Spark Application能够在集群中并行执行,其关键是抽象出RDD的概念(详见RDD 细解),也使得Spark Application的开发变得简单明了.下图浓缩了Spark的编程模型. w ...
- iOS 带IAP提交注意事项及无法submit for review的解决方案
原地址:http://blog.sina.com.cn/s/blog_71ce775e0101dl4a.html 最近项目接触到了苹果的程序内购(IAP),碰到不少问题,参考了很多帖子才得以解决.在此 ...
- REDIS 内存满时删除策略
REDIS 内存满时删除策略
- MySQL触发器 trigger之after与before区分
after:是先完毕数据的增删改,然后再触发.触发的语句晚于监视的增删改,无法影响前面的增删修改作.也就是说先插入订单记录.再更新商品数量.当商品数量少于订单数量时造成爆库. before:先完毕触发 ...
- 非常酷的 Javascript 简单调试工具Blackbird
Blackbird 是一个开源的 Javascript 调试工具,默认提供一种非常酷的方式展现 Javascript 调试信息,如下图,效果如何呢? 在我们的日常的学习或工作中,经常都会接触到 Jav ...
- 用Emit技术替代反射
之前在上篇博客说到用表达式来替代反射机制,可以获得较高的性能提升.这篇我们来说说用Emit技术来替代反射. System.Reflection.Emit命名空间类可用于动态发出Microsoft中间语 ...
- JavaScript的特殊函数
1.匿名函数 onclick=function(){}就是匿名函数. 2.匿名函数的回调函数 <script> <span style="white-space:pre&q ...