对象发布(publish)
发布一个对象是使它能够在被当前范围之外的代码所使用。比如创建一个对象之后,提供一个非私有方法返回这个对象的引用,或者把它传递到其他类的方法中。下面是常见的发布对象的例子:
1.把对象的引用保存到静态公有域中 public static List<Object> objectList; public void initList() { objectList =New ArrayList<Object>(); }
在上面的代码中,任何类和线程都可以访问objectList,当objectList通过initList方法实例化一个ArrayList对象后,并把引用赋值给objectList,那么这个ArrayList对象就发布出去了。同时发布一个对象还可能间接的发布了其他对象。比如objectList添加了一个object1,那么object1也被发布出去了。因为任何类或线程都可以通过遍历objectList访问并修改object1。
2.通过非私有方法把对象发布出去 private String[] strArray = {"data1","data2"}; public String[] getStrArray () { return strArray ; }
通过getStrArray方法,任何类或者线程都可以访问和修改strArray,此时strArray对象也被发布出去了。但是这里strArray变量定义为私有对象,通过getStrArray方法,使得strArray超出了它的作用范围,由私有变成公有的了。
发布一个对象,同样发布了该对象的所有非私有域引用的对象,甚至整个非私有域的引用链中的对象,都发布出去了。
对象逸出(escape)
一个对象超出了她应有的作用域,或者未被构造完全就发布了,称为对象逸出。很多情况下我们不希望对象以及对象的内部属性不被暴露出去,但是在其他一些情况中,我们又必须使对象发布出去,但是如果不经意中把对象的内部状态也发布出去了,就使得其他线程可以修改这个对象的内部状态,造成不可预知的线程安全性风险,同时,如果对象没有构造完全就发布出去了同样会危及线程安全。上面第2个代码就是把私有对象发布出去而导致对象逸出。下面是一种对象未构造完全就发布造成的对象逸出:
对象在构建期间逸出 public class ThisEscape() { public ThisEscape(EventSource event) { event.register(new EventListener(){ public void onEvent(Event e){ doSomeThing(e);} }) } }
这段代码的本意是注册监听器,通过匿名内部类发布EventListener,但是由于内部类会持有封装它的外部类的引用this,所以ThisEscape的一个对象也发布出去了。从而导致对象未构造完全就发布出去的逸出。对象只有在构造函数返回后才处于可预知的,真正稳定的状态。所以从构造方法中发布对象,它发布的对象是未构建全的对象,即使是在对象的最后一行发布也是如此。一个非常常见的在构造期对象逃逸的错误,是在构造期启动线程,无论是显式的还是隐式的(因为thread或ruanable是所属对象的内部类),this引用总是被新线程所持有。
如何安全发布对象
为了安全的发布对象,对象的引用以及对象的状态必须同时对其他线程可见。那么如何才能安全的发布对象呢,下面是正确发布对象的几种方法
1.通过静态初始化器初始化对象的引用
常见的例子是在构造方法中注册监听器或启动线程。如果非要在构造函数中启动线程或启动线程,可以使用私有的构造函数和公有的工厂方法,比如上面注册监听器的例子:
public class SafeListener() { private final EventLister listener; private SafeListener() { listener= new EventLister () { public void onEvent(Event e) { doSomething(e); } } } public static SafeListener getInstance(EventSource source) { SafeListener safeListener = new SafeListener(); source.resgister(safeListener ); return safeListener ; } }
2.线程封闭
需要线程同步的基础是线程间共享对象和数据,如果线程不共享数据和对象,对象只封闭在线程内部,那么就不需要线程同步,也就不需要发布对象,即使对象是不线程安全的。
2.1 线程限制
通过编程人员维护对象安全发布,而没有变量修饰词或线程本地变量把对象限制在线程上。这种方法非常容易出错,因此也叫非正式线程限制(Ad-hoc线程限制)。下面是几种常见的线程限制方法:
volatile变量
一张典型的线程限制是使用volatile关键词。只要你确保只要一个线程修改volatile变量,那么volatile变量的读取-修改-写入操作就限制在一个线程中,避免了线程的竞争条件。并且volatile的可见性可以保证其他线程能看到变量的最新值,因此是线程安全的。
栈限制
即使变量的作用域只局限于线程内部,比如局部变量。
2.3 threadlocal
threadlocal允许每个线程与对象关联在一起,它提供了set/get方法,为每个使用它的线程维护一份单独的拷贝。threadlocal通常使用在防止基于可变的单例或全局变量中出现不正确的共享。
2.4 不变对象
不变对象一旦被创建就不能更改,所以永远是线程安全的。所谓的不变对象,必须满足3个条件:创建后状态不能更改,所有域必须是final的,被正确构建(构造期间没有发生对象逸出)。不变对象一般是一些比较简单的对象,它的内部状态在对象构造方法里就赋值了。
3.将对象的引用存储到被正确同步的变量或对象中
具体有下面几种做法:
将对象的引用存储到volatile域或AutomaticReference中;
将对象的引用存储到正确创建对象的final域中
将对象的引用存储到被锁正确保护的域中,比如线程安全的容器 vector,synchronizedMap,concurrentMap,synchronizedList,synchronizedSet,BlockingQueue,concurrentLinkedQueue等。
相关推荐
3.5.3 安全发布的常用模式 3.5.4 事实不可变对象 3.5.5 可变对象 3.5.6 安全地共享对象 第4章 对象的组合 4.1 设计线程安全的类 4.1.1 收集同步需求 4.1.2 依赖状态的操作 4.1.3 状态的所有权 4.2 实例...
- **特性**: 创建结束时,若对象被安全发布,则所有线程都可以获取到final字段在构造过程中设置的值,即使没有`synchronized`关键字。 #### 三、保护共享数据 在多线程环境中,保护共享数据至关重要,以确保数据...
3.5.3 安全发布的常用模式 3.5.4 事实不可变对象 3.5.5 可变对象 3.5.6 安全地共享对象 第4章 对象的组合 4.1 设计线程安全的类 4.1.1 收集同步需求 4.1.2 依赖状态的操作 4.1.3 状态的所有权 4.2 实例...
14. **对象发布的注意事项:** 发布对象时必须确保其完全构造完成,以防止对象逸出导致的线程安全问题。 15. **使用ThreadLocal确保线程封闭性:** 通过`ThreadLocal`可以在每个线程中存储独立的副本,从而避免线程...
本书的读者是那些具有一定Java编程经验的程序员、希望了解Java SE 5,6在线程技术上的改进和新特性的程序员,以及Java和并发编程的爱好者。 目录 代码清单 序 第1章 介绍 1.1 并发的(非常)简短历史 1.2 线程的...
Java并发编程实践笔记是一份关于Java并发编程的实践笔记,涵盖了多种关于线程安全、并发编程的实践经验和原则。下面是从笔记中总结的知识点: 1. 保证线程安全的三种方法:不要跨线程访问共享变量,使用final类型的...
《java并发编程实战》读书笔记-第3章-对象的共享,脑图形式,使用xmind8制作 包括可见性、发布与逸出、线程封闭、不可变性、安全发布等内容
读者对象涵盖了Java开发工程师、架构师、并发编程爱好者以及大专院校师生,因此书中内容覆盖面广泛,从基础知识到高级技术均有涉及。 在前言中,作者还谈到了个人的成长经历和写书的初衷,让读者看到了一本技术书籍...
第5章安全发布对象 第6章线程安全策略 第7章J.U.C之AQS 第8章J.U.C组件拓展 第9章线程调度-线程池 第10章多线程并发拓展 第11章高并发之扩容思路 第12章高并发之缓存思路 第13章高并发之消息队列思路 第14章高并发之...
- 安全发布:确保对象初始化完成后再被其他线程访问。 - 不可变对象:一旦创建,其状态就不能改变,天然线程安全。 - 状态封闭:确保对象的状态只在其创建者线程内修改。 - 双重检查锁定(Double-Check Locking...
java 并发编程实践001 002 两个文件全部下载后 用 7z解压 第1章 介绍 1.1 并发的(非常)简短历史 1.2 线程的优点 1.3 线程的风险 1.4 线程无处不在 第1部分 基础 第2章 线程安全 2.1 什么是线程安全性 2.2 原子...
此外,还涉及了对象共享,包括可见性、发布与逸出、线程封闭、不可变性以及安全发布。 第二部分“构造并发应用程序”则深入到任务执行、并发控制和错误处理等主题。如何在线程中执行任务,使用Executor框架,以及...
操作队列是 iOS 平台下的并发编程技术之一。操作队列可以帮助开发者更好地管理并发任务,提高程序的性能和响应速度。操作队列的设计需要考虑到操作对象的执行行为、操作对象的子类化、自定义操作对象的执行行为等...
为了避免这些问题,可以通过确保对象的完全初始化来实现安全发布。例如,在构造函数中注册回调或者延迟对象的发布。 - **好处**:确保所有线程看到的是一个完整初始化的对象,从而避免潜在的并发问题。 7. **不可...