initialValue函数 : initialValue函数用来设置ThreadLocal的初始值,该函数在调用get函数的时候会第一次调用,但是如果一开始就调用了set函数,则该函数不会被调用。
get函数:该函数用来获取与当前线程关联的ThreadLocal的值,如果当前线程没有该ThreadLocal的值,则调用initialValue函数获取初始值返回。
set函数: set函数用来设置当前线程的该ThreadLocal的值。
remove函数:remove函数用来将当前线程的ThreadLocal绑定的值删除。
public class thread_local2 {
private static final ThreadLocal<Integer> value = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 0;
}
};
static class MyThread extends Thread{private int index;
public MyThread(int index){
this.index = index;
}
public void run(){
System.out.println("线程"+index+":的初始value="+value.get());
for (int i = 0; i < 10; i++) {
value.set(value.get()+i);
if(i == 5){
value.remove();
}
}
System.out.println("线程"+index+":的累加始value="+value.get());
}
}
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new Thread(new MyThread(i)).start();
}
}
}
输出:
线程3:的初始value=0
线程3:的累加始value=30
线程0:的初始value=0
线程4:的初始value=0
线程2:的初始value=0
线程0:的累加始value=30
线程1:的初始value=0
线程1:的累加始value=30
线程4:的累加始value=30
线程2:的累加始value=30
JDK8的ThreadLocal的get方法的源码:
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();
}
其中getMap()的源码:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
简单解析一下,get方法的流程是这样的:
设计思路:每个Thread维护一个ThreadLocalMap映射表,这个映射表的key是ThreadLocal实例本身,value是真正需要存储的Object。
ThreadLocalMap是使用ThreadLocal的弱引用作为Key的。
ThreadLocal会引发内存泄露,他们的理由是这样的:
如上图,ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal没有外部强引用引用他,那么系统gc的时候,这个ThreadLocal势必会被回收,这样一来,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value,如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:
ThreadLocal Ref -> Thread -> ThreaLocalMap -> Entry -> value
永远无法回收,造成内存泄露。
JDK设计中的防范: 如ThreadLocalMap的getEntry函数的流程:(如果e为null或者key不一致则向下一个位置查询,如果下一个位置的key和当前需要查询的key相等,则返回对应的Entry,否则,如果key值为null,则擦除该位置的Entry,否则继续向下一个位置查询)set操作也有类似的思想,将key为null的这些Entry都删除,防止内存泄露。
以上有一个前提,要调用ThreadLocalMap的*genEntry函数或者set函数。* 1. 所以很多情况下需要使用者手动调用ThreadLocal的remove函数,手动删除不再需要的ThreadLocal,防止内存泄露。2. JDK建议将ThreadLocal变量定义成private static的,这样的话ThreadLocal的生命周期就更长,由于一直存在ThreadLocal的强引用,所以ThreadLocal也就不会被回收,也就能保证任何时候都能根据ThreadLocal的弱引用访问到Entry的value值,然后remove它,防止内存泄露。
参考:https://www.zhihu.com/question/23089780 winwill2012
@Component
public class HostHolder {
private static ThreadLocal<User> users = new ThreadLocal<User>();
public User getUser() {
return users.get();
}
public void setUser(User user) {
users.set(user);
}
public void clear() {
users.remove();;
}
}
当用户登陆的时候,使用拦截器将当前用户的信息set进去。
@Component
public class PassportInterceptor implements HandlerInterceptor {
@Autowired
private LoginTicketDAO loginTicketDAO;
@Autowired
private UserDAO userDAO;
@Autowired
private HostHolder hostHolder;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
String ticket = null;
if (httpServletRequest.getCookies() != null) {
for (Cookie cookie : httpServletRequest.getCookies()) {
if (cookie.getName().equals("ticket")) {
ticket = cookie.getValue();
break;
}
}
}
if (ticket != null) {
LoginTicket loginTicket = loginTicketDAO.selectByTicket(ticket);
if (loginTicket == null || loginTicket.getExpired().before(new Date()) || loginTicket.getStatus() != 0) {
return true;
}
User user = userDAO.selectById(loginTicket.getUserId());
hostHolder.setUser(user);
}
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
if (modelAndView != null && hostHolder.getUser() != null) {
modelAndView.addObject("user", hostHolder.getUser());
}
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
hostHolder.clear();
}
这样,当我们需要使用当前用户信息时,直接从这个hostHolder中取出就行了。
@Autowired
HostHolder hostHolder;
hostHolder.getUser().......
手机扫一扫
移动阅读更方便
你可能感兴趣的文章