把对象封装到一个线程里,只有一个线程能够看到该对象,那么就算这个对象不是线程平安的,也不会呈现任何线程问题,因为它只能在一个线程中被拜访。
  • Ad-hoc线程关闭:程序控制实现,十分软弱,最蹩脚,疏忽。
  • 堆栈关闭:简略的说就是局部变量,无并发问题。多线程拜访同一个办法时,办法中的局部变量会被拷贝一份到线程栈中。办法的局部变量不是被多线程共享的,不会呈现线程平安问题,能用局部变量就不要用全局变量,全局变量容易产生并发问题,留神全局变量不是全局常量。
  • ThreadLocal线程关闭:Java中提供一个ThreadLocal类来实现线程关闭,这个类使线程中的某个值与保留值的对象关联起来

ThreadLocal

ThreadLocal类提供的办法

外围的五个操作:创立,创立并赋初始值,赋值,取值,删除
  • 创立:
private final static ThreadLocal<Object> threadLocal = new ThreadLocal<Object>;
  • 创立并赋初始值
private final static ThreadLocal<String> threadLocal=new ThreadLocal<String>(){        @Override        protected String initialValue() {            return "入门小站";        }};
  • 赋值
threadLocal.set("入门小站");
  • 取值
threadLocal.get();
  • 删除
threadLocal.remove();

实现原理

首先ThreadLocal是一个泛型类,保障能够承受任何类型的对象。

一个线程内能够存在多个ThreadLocal,ThreadLocal外部保护了一个Map,这个Map不是HashMap,而是ThreadLocal实现的一个ThreadLocalMap的动态外部类。咱们应用的get(),set()办法其实是调用了这个ThreadLocalMap类对应的get(),set()

public T get() {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null) {            ThreadLocalMap.Entry e = map.getEntry(this);            if (e != null) {                @SuppressWarnings("unchecked")                T result = (T)e.value;                return result;            }        }        return setInitialValue();    }    public void set(T value) {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);        else            createMap(t, value);}
调用ThreadLocal的set办法时,先获取以后的线程Thread t = Thread.currentThread();,而后获取以后线程保护的ThreadLocalMap。如果ThreadLocalMap不存在则初始化。

ThreadLocalMapmap.set(this, value);第一个参数是this,this指的是以后的ThreadLocal,就是下面代码外面的threadLocal变量。

最终的变量是放在以后线程的ThreadLocalMap中,并不是存在ThreadLocal上,ThreadLocal能够了解成传递关系的。

内存透露问题

ThreadLocalMap中应用的keyThreadLocal的弱援用,弱援用的特点是,如果这个对象只存在弱援用,那么在下一次垃圾回收的时候必然会被清理掉。

所以ThreadLocal没有被强援用的状况下,在垃圾回收的时候会被清理掉,然而value却是强援用,不会被清理,这样的话就出呈现keynullvalue

ThreadLocalMap实现中曾经思考了这个状况,在调用set,get,remove办法的时候会清理掉keynull的记录。如果呈现了内存透露,那就是说在keynull后,没有手动调用remove办法,并且之后也不再调用set,get,remove办法。

内存透露解决方案

将ThreadLocal变量定义成private static的,这样的话ThreadLocal的生命周期就更长,因为始终存在ThreadLocal的强援用,所以ThreadLocal也就不会被回收,也就能保障任何时候都能依据ThreadLocal的弱援用拜访到Entry的value值,而后remove它,避免内存泄露。

如何保障两个同时实例化的ThreadLocal对象有不同的threadLocalHashCode属性

在ThreadLocal类中,还蕴含了一个static润饰的AtomicInteger([tmk]提供原子操作的Integer类)成员变量(即类变量)和一个static final 润饰的常量(作为两个相邻nextHashCode的差值)。因为nextHashCode是类变量,所以每一次调用ThreadLocal类都能够保障nextHashCode被更新到新的值,并且下一次调用ThreadLocal类这个被更新的值依然可用,同时AtomicInteger保障了nextHashCode自增的原子性。

ThreadLocal利用

场景一

Web我的项目公共参数从controller层传递到service层,再从service层传递到mapper层,或者从service层传递到其余的工具类当中。为了防止参数简单的传递,在controller中将曾经封装好的参数放入ThreadLocal中,在其余层调用时间接通过ThreadLocal对象获取。在办法完结时,定义拦截器(HandlerInterceptorAdapter)(或者Filter)进行ThreadLocal的remove办法。

场景二

在须要登录的零碎中用户信息经常存在Sessiontoken。比方咱们要从Session中获取用户信息须要在接口参数中加上HttpServletRequest对象,而后调用 getSession办法,且每一个须要用户信息的接口都要加上这个参数,能力获取Session,比拟麻烦。

这个时候咱们就能够用ThreadLocal,在拦截器(HandlerInterceptorAdapter)(或者Filter)中解析获取用户信息,而后保留到ThreadLocal,业务逻辑间接在ThreadLocal中获取就能够了。

【关注微信公众号:【入门小站】解锁更多知识点】