这次想总结ThreadLocal这个东西,也是由于项目中使用到了它去帮助保存会话信息。传统的(或者说我在学校的时候)方法,大多是用服务端的session保存会话,与浏览器端的cookie协作去追踪这个会话。而现在更多的使用ThreadLocal去保存会话的信息,这是由于ThreadLocal天生带有线程安全的特性,并且仅仅在一个线程内共享变量(恰好符合多用户多会话请求这一场景),这就使得这种方式用起来十分顺手和简单。
1. 项目中的使用:
首先记录一下项目中ThreadLocal是如何保存用户会话信息的:
1 private final String KEY_USER = "user"; 2 private final String KEY_TICKET = "ticket"; 3 private final String KEY_ROLEKEY = "roleKey"; 4 5 private static class Inner{ 6 static LocalThreadUtils localThreadUtils = new LocalThreadUtils(); 7 } 8 9 public static LocalThreadUtils getInstance(){ 10 return Inner.localThreadUtils; 11 } 12 13 14 private ThreadLocal<Map<String, Object>> threadLocal = ThreadLocal.withInitial(() -> Maps.newHashMap()); 15 16 17 public void cleanContext(){ 18 threadLocal.get().clear(); 19 }
核心就是这样一个内部类的单例模式,产生的单例有一个theadLocal私有变量,用ThreadLocal修饰,内部是一个Map,而这个map可以由我们自己去存放用户的各种信息,比如ticket,角色role,用户信息user等。
ThreadLocal这种方式的优点在于实现简单,并不需要我们在每一层传递request,也不用考虑httpSession的作用域,我们可以再任意层(controller,service等)直接访问当前线程信息,从而取出会话信息。
2. 实现方式和原理:
ThreadLocal类是如何为每个线程创建一个变量的副本的:
首先,在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本(即T类型的变量)。
初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。
然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。
其他想要说的:
ThreadLocal真的不是用来解决对象共享访问问题的,而主要是提供了保持对象的方法和避免参数传递的方便的对象访问方式。
1、每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。
2、将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程生命周期内执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦。
写在最后:这里只是对ThreadLocal做一个小小的总结,因为在之前的项目中从来没有真正使用过,只是字面上了解他的含义。很多知识确实要通过实践才能有更深刻的理解。