0.概念理解
对象状态:就是对象当中的各个成员变量;
对象发布:就是将对象的引用交给别人;
安全发布:创建对象需要一段时间,等彻底创建完了才把引用给别人(其他线程);
逸出/逃逸:不应该发布的对象被发布了;
不正确构造:this在构造过程中逸出(即使在构造函数最后一行也不行);
线程封闭:不要让不安全的对象被多线程共享;
不可变对象:只有一种状态的对象;
事实不可变对象:状态在发布后不会改变的可变对象;
1.为甚么存在对象共享的问题?
多个线程之间需要通信,需要操作同一组数据,以达到多线程协作的目的。
2.多线程之间共享对象时会存在什么样的问题?
- 线程A在使用对象状态的同时线程B在修改对象状态,导致使用时读取到的对象状态是新老版本各一半,造成错误;
- 一个线程在修改了对象状态后,其他线程不能够及时看到。
3.volatile的原理与应用
- 原理:禁止指令重排,禁用寄存器等线程间不共享的缓存,不加锁不阻塞,成本只是略高;
- 应用:作为状态标记以判断是否退出循环;
4.发布对象的几种情况
- 将对象引用保存至公有静态变量;
- 从非私有方法中返回一个引用;
- 传递发布:发布对象的公有域都会被发布;
- 发布一个内部类实例(隐含引用外部类this),不正确构造;
5.线程封闭的方式、应用场景与注意事项
- 方式:
- Ad-hoc(自己注意)、栈封闭(局部变量)、ThreadLocal(资源副本)
- 应用场景:
- Swing的可视化组件与数据模型的事件分发线程;
- JDBC的Connection对象;
- 注意:
- 防止逸出:不要让外部传入的对象引用到栈内对象;
- ThreadLocal仅仅用在分配操作开销高的对象上,否则用栈封闭即可;
- ThreadLocal的滥用:将使用该机制的代码与框架代码耦合,在类之间引入隐含的耦合性,降低代码可重用性;
6.不可变对象不变性条件
- 对象创建以后其状态就不能修改;
- 对象的所有域都是final类型;
- 对象是正确创建的(构造时this未逸出);
7.使用volatile+不可变对象保证线程安全的原理
不可变对象作为容器存放多个状态,要同步改动所有状态只有一种方式:重新new一个容器不可变对象,再把引用指过去,要及时可见则加volatile;
8.安全发布的几种办法
- 在静态初始化函数中初始化一个对象引用;
- 将对象引用保存在volatile或AtomicReference对象中;
- 将对象引用保存到某个正确构造对象的final域中;
- 将对象引用保存到一个由锁保护的域中;
9.保证线程安全地共享对象的几种方法
- 线程封闭:修改操作封闭在单线程;
- ThreadLocal
- volatile+发布不可变对象
- 安全发布事实不可变对象
其他需要注意的问题
- 若无同步,编译器、处理器以及运行时都可能对操作执行顺序进行重排;
- long与double写操作非原子,需要加volatile或锁;
- 服务端程序调试启动JVM必须加-server;
- 在构造函数中调用可改写方法可能使this逸出,可使用私有构造函数和公共工厂方法,确保初始化好了之后再发布;
- Java内存模型中,final域能保证初始化过程安全性,也就是正确创建;