java 封闭实例_Java线程封闭

把对象封装到一个线程里,只有一个线程可以看到该对象,那么就算这个对象不是线程安全的,也不会出现任何线程问题,因为它只能在一个线程中被访问。

Ad-hoc线程封闭:程序控制实现,非常脆弱,最糟糕,忽略。

堆栈封闭:简单的说就是局部变量,无并发问题。多线程访问同一个方法时,方法中的局部变量会被拷贝一份到线程栈中。方法的局部变量不是被多线程共享的,不会出现线程安全问题,能用局部变量就不要用全局变量,全局变量容易发生并发问题,注意全局变量不是全局常量。

ThreadLocal线程封闭:Java中提供一个ThreadLocal类来实现线程封闭,这个类使线程中的某个值与保存值的对象关联起来

ThreadLocal

ThreadLocal类提供的方法

91a2bbc0425eb91cdce017140df4a671.png

核心的五个操作:创建,创建并赋初始值,赋值,取值,删除

创建:

private final static ThreadLocal threadLocal = new ThreadLocal;

创建并赋初始值

private final static ThreadLocal threadLocal=new ThreadLocal(){

@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不存在则初始化。

ThreadLocalMap的map.set(this, value);第一个参数是this,this指的是当前的ThreadLocal,就是上面代码里面的threadLocal变量。

最终的变量是放在当前线程的ThreadLocalMap中,并不是存在ThreadLocal上,ThreadLocal可以理解成传递关系的。

内存泄漏问题

8066c14f1fec36e0f9496b4f26001dde.png

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

所以ThreadLocal没有被强引用的情况下,在垃圾回收的时候会被清理掉,但是value却是强引用,不会被清理,这样的话就出出现key为null的value。

ThreadLocalMap实现中已经考虑了这个情况,在调用set,get,remove方法的时候会清理掉key为null的记录。如果出现了内存泄漏,那就是说在key为null后,没有手动调用remove方法,并且之后也不再调用set,get,remove方法。

内存泄漏解决方案

将ThreadLocal变量定义成private static的,这样的话ThreadLocal的生命周期就更长,由于一直存在ThreadLocal的强引用,所以ThreadLocal也就不会被回收,也就能保证任何时候都能根据ThreadLocal的弱引用访问到Entry的value值,然后remove它,防止内存泄露。

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

在ThreadLocal类中,还包含了一个static修饰的AtomicInteger([əˈtɒmɪk]提供原子操作的Integer类)成员变量(即类变量)和一个static final 修饰的常量(作为两个相邻nextHashCode的差值)。由于nextHashCode是类变量,所以每一次调用ThreadLocal类都可以保证nextHashCode被更新到新的值,并且下一次调用ThreadLocal类这个被更新的值仍然可用,同时AtomicInteger保证了nextHashCode自增的原子性。

ThreadLocal应用

场景一

Web项目公共参数从controller层传递到service层,再从service层传递到mapper层,或者从service层传递到其他的工具类当中。为了避免参数复杂的传递,在controller中将已经封装好的参数放入ThreadLocal中,在其他层调用时直接通过ThreadLocal对象获取。在方法结束时,定义拦截器(HandlerInterceptorAdapter)(或者Filter)进行ThreadLocal的remove方法。

场景二

在需要登录的系统中用户信息常常存在Session和token。比如我们要从Session中获取用户信息需要在接口参数中加上HttpServletRequest对象,然后调用 getSession方法,且每一个需要用户信息的接口都要加上这个参数,才能获取Session,比较麻烦。

这个时候我们就可以用ThreadLocal,在拦截器(HandlerInterceptorAdapter)(或者Filter)中解析获取用户信息,然后保存到ThreadLocal,业务逻辑直接在ThreadLocal中获取就可以了。

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值