多线程-- 四.不可变对象和线程封闭带来的线程安全
不可变对象和线程封闭带来的线程安全
不可变对象
当一个类的对象满足下面条件时,这个对象可以成为不可变对象
通过在某些情况下,将不会修改的类对象设计为不可变对象,来让对象成为线程安全的.把对象编程不可变对象,就不会出现线程安全问题了
1.对象创建以后,其状态就不能更改.
2.对象所有域都是final类型.
3.对象是正确创建的(在对象创建期间,this引用没有逸出).
变为不可变对象方法:
1.final关键字修饰
修饰类,类不可以被继承.
修饰方法,方法不可以被重写.
修饰基本数据类型,不可以更改值.
修饰引用数据类型,不可以指向新的地址,但是可以更改值.(也可以更改,见方法2)
2.Collections.unmodifiableXXX()方法 XXX:List,Set,Map…..
用这个方法修饰的对象,就不可以被更改了
这样,map里面修改值也不可以了,直接报错.其实put方法是可以执行的,只是直接抛异常了.
这是因为这个方法源码里面,它返回一个自定义的map,这个map里面就是把修改的一些方法,直接写为了抛异常.
3.google的Guava包中的方法ImmutableXXX XXX:List,Set,Map…..
这个跟上面的基本类似,也是直接报错,并且方法上也提示了横线
线程封闭
线程封闭的几种方式:
1.Ad-hoc线程封闭:程序控制实现,最糟糕,忽略,不建议使用.
2.堆栈封闭:局部变量,无并发问题.(这个其实也是我们经常用的,说白了,就是能用局部变量的,就不用全局变量.全局变量会产生线程问题.)
3.ThreaLocal线程封闭:特别好的封闭方法
创建ThreadLocal对象:
ThreadLocal<> requestHolder = new ThreadLocal<>();
ThreadLocal类提供的几个方法:
public T get() { }: get()方法是用来获取ThreadLocal在当前线程中保存的变量副本.
public void set(T value) { }: set()用来设置当前线程中变量的副本.
public void remove() { }: remove()用来移除当前线程中变量的副本.
protected T initialValue() { }: initialValue()是一个protected方法,一般是用来在使用时进行重写的.