第六章 保障线程安全的设计技术(不必借助锁的情况下保障线程安全)
6.1 Java运行时存储空间
- Java运行时空间可分为堆空间、栈空间和非堆空间。其中,堆空间和非堆空间是可以被对个线程共享的,而栈空间则是线程的私有空间,每个线程都有其栈空间,并且一个线程无法访问其他线程的栈空间。
- 堆空间(Heap space)用于存储对象,即创建一个实例的时候该实例所需的存储空间是在堆空间中进行分配。
- 实例变量以及引用型实例变量所引用的对象是可以被多个线程共享的。不管引用对象的变量的作用域如何(局部变量、实例变量和静态变量),对象本身总是存储在堆空间中的。
- 基础类型变量和引用类型变量的变量值都是直接存储在栈帧中的。引用型变量的值相当于被引用对象的内存地址,而引用型变量所引用的对象仍然在堆空间中。由于一个线程无法访问另外一个线程的栈空间。因此,线程对局部变量以及对只能通过当前线程的局部变量才能访问到的对象进行的操作具有固有的线程安全性。
- 非堆空间用于存储常量以及类的元数据。类的元数据包括类的静态变量、类有哪些方法以及这些方法的元数据(包括名称、参数和返回值等)。非堆空间也是多个线程之间共享的存储空间。类的静态变量在非堆空间中的存储方式与局部变量在栈空间的存储方式相同,即这些空间中仅存储变量的值本身,而引用型变量所引用的对象仍然存储在堆空间中。
6.2 无状态对象
- 无状态对象不含任何实例变量、且不包含任何静态变量或者其包含的静态变量都是只读的(常量)。
- 无状态对象的客户端代码在调用该对象的任何方法时都无须加锁。
- 无状态对象自身的实现也无需加锁。
6.3 不可变对象
- 不可变对象是指一经创建其状态就保持不变的对象。需要满足以下所有的条件。
- 类本身使用final修饰。(防止子类)
- 所有字段都是用final修饰。(被修饰字段初始化安全,即final修饰的字段在对其他线程可见时,它必定是初始化完成)
- 对象在此初始话过程中没有逸出。
- 任何字段,若其引用了其他状态可变的对象(如集合、数组),则这些字段必须是private修饰的,并且这些字段值不能对外暴露。
6.4 线程特有对象
- ThreadLocal<T>类相当于线程访问其线程持有对象的代理(proxy),即各个线程通过这个对象可以创建并访问各自的线程特有对象,其类型参数T指定了相应线程特有对象的类型。
6.5 装饰器模式
- 装饰器模式可以用来实现线程安全,其基本思想是为非线程安全对象创建一个相应的线程安全的外包装对象,客户端代码不直接访问非线程安全对象而是访问其外包装对象。
网友评论