1. 局部变量
使得我们不必借助锁的的情况下来保障线程安全,可以避免锁导致的问题和开销
java运行时空间可以分为栈区,堆区和方法区(非堆空间)
-
栈空间)(Stack Space),为线程的执行准备一段固定大小的存储空间,每个线程都有独立的线程栈空间,创建线程时就为线程分配栈空间,在线程栈中没调用一个方法就给方法分配一个栈帧,栈帧用于存储方法的局部变量,返回值等私有数据,即局部变量存储在栈空间中,基本类型变量也是存储在栈空间中,饮用类型变量值也是存在栈空间,引用的对象在堆中,由于线程是相互独立的,一个线程不能访问类外一个线程的栈空间,因此线程对局部变量以及智能通过当前线程的局部变量才能访问的对象进行的操作具有固定的线程安全性
-
堆空间(Heep Space) 用于存储对象,是在java虚拟机启动时分配的一段可以动态扩容的内存空间,创建对象时在堆空间中给对象分配空间,实例变量就是存储在堆空间中,堆空间是多个线程之间可以共享的空间,因此实例变量可以被多个线程共享,多个线程同时操作实例变量,可能存在线程安全问题
-
非堆空间(Non-Hoop Space)用于存常量,类的元数据等,非堆空间也是在JVM启动时分配的一段可以动态扩容的存储空间,类的元数据包括静态变量,类有哪儿些方法及这些方法的元数据(方法名,参数,返回值等),非堆空间也是多个,线程可以共享,因此访问非堆空间中的静态变量也可能存在线程安全问题
堆空间和非堆空间是线程可以共享的空间,即实例变量与静态变量时线程可以共享的,可能存在线程安全问题,栈空间是线程私有的存储空间,局部变量存储在栈空间中,局部变量具有固有的线程安全性
2.无状态对象
对象就是数据集对数据操作的封装,对象所包含的数据成为对象的状态(State)。实例变量与静态变量成为状态变量,
如果一个类的同一个实例被多个线程共享并不会使这些线程存在共享的状态,那么该类的实例成为无状态对象(Stateless Object) ,如果一个类的实例被多个线程共享会使这些线程存在共享状态,那么该类的实例成为有状态对象,实际上无状态对象就是不包含任何实例变量的对象,也不包含任何静态变量的对象。
线程安全问题的前提是多个线程存在共享的数据,实现线程安全的一种办法就是避免在多个线程之间共享数据,使用无状态对象就是这种方法
3.不可变对象
不可变对象是指一经创建他的状态就保持不限,不可变兑现也具有固有的线程安全性,当不可变对象现实实体的状态发生变化时,系统会创建一个新的不可变对象。就如String字符串对象,一个不可变对象需要满足一下条件
- 类本身使用final修饰,就是防止通过创建子类的形式来改变它的定义,
- 所有的字段都是final修饰的,final字段在创建对象时必须显示初始化不能被修改
- 如果字段引用了其他状态可变的对象(集合,数组),则这些字段必须是private私有的
不可变对象主要的应用场景 - 被建模对象的状态变化不频繁
- 同时对一组相关数据进行写操作,可以应用不可变队形,既可以保障他的原子性,也避免锁的使用
- 使用不可变对象作为安全可靠的Map,HashMap键值对的存储位置与建的hashCode有关,如果建的7内部状态发生变化会导致键的哈希码不同,可能会影响键值对的存储位置,如果hashMap的键是不可变对象,则hashCode方法的返回值是不变的,存储的位置就是固定的
4. 线程特有对象
我们可以选择不共享非线程安全的对象,对于非线程安全的对象,每个线程创建一个该对象的实例,各个线程访问各自创建的实例,一个线程不能访问另外一个线程创建的实例这种各个线程创建各自的实例,一个实例只能被一个线程访问的对象就成为线程特有对象,线程特有对象他既保障了对非线程对象的访问的线程安全,又避免了锁的开销,线程特有对象具有固有的线程安全性
ThreadLocal 类相当于线程访问其特有对象的代理,即各个线程通过ThreadLocal对象可以创建访问各自的线程特有的对象,泛型T指定了线程特有对象的类型,一个线程可以使用不同的ThreadLocal实例创建并访问不同的相册特有对象
ThreadLocal实例为每个访问他的线程都关联了一个该线程特有的对象,ThreadLocal都有一个当前线程与特有实例之间的一个关联,
5.装饰器
装饰器模式也可以用来实现线程安全,基本思想是为非线程安全的对象,创建一个相应的线程安全的外包装对象,客户端代码不直接访问非线程安全的对象,而是访问他的外包装对象,外包装对象与非线程安全的对象具有相同的接口,即外包装对象的使用方式与非线程安全对象的使用方式相同,而外包装对象内部通常会借助锁,以线程安全的方式调用相应的线程安全的对象的方法。
在java.util.Collection工具中提供了一组synchronizedXXX(xxx)可以把不是线程安全的xxx集合转换成线程安全的集合,他就是采用了这种装饰器模式,这个方法的返回值就是指定集合的外包装对象,这类集合又称为同步集合