吴密的博客

每天进步一点点
posts - 12, comments - 1, trackbacks - 0, articles - 1

threadLocal--简单本地缓存

Posted on 2010-09-10 13:44 xiaolang 阅读(1978) 评论(0)  编辑  收藏

 

首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。各个线程中访问的是不同的对象。

另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本。

个人版中有一个比较好的应用场景,就是为会员查询服务做的catche

在用户的一次请求中需要多次查询用户的卡信息,会多次去调用远程服务,造成性能上的浪费。如果能在本地做一个简单的cache,就可以省去远程调用的开销,就可以如下图所示

                                                                 
                       

从图中可以看出,优化后的将一部分cache存在本地,就省去了远程调用的开销,从一定程度上减轻了会员核心的压力。

Cache
一般都有一个刷新时间的问题,时间长了,信息可能不准确,时间短了,比较耗费性能。最好的情况是这个cache过期了,不需要使用了,立即把清除,废弃,这个最理想的情况。

大家应该知道,用户从发起请求,到服务器响应的这个过程中,在服务器中是在一个线程中的。如果我们吧查询出来的东西放到这个线程中,到用户请求结束时,把这些东西清理掉,应该是一个不错的cache方案。

接下来的问题就简单了,我们只需要把查询的东西放到threadLocal就可以了。

看大牛李磊如何实现这一点

1.   
首先需要初始化一个threadLocal

    /** 用于将会员信息保存在本地的线程变量*/

    private static ThreadLocal<List<UserInfo>>            localUserInfo      = new ThreadLocal<List<UserInfo>>() {
                                                                                 protected synchronized List<UserInfo> initialValue() {
                                                                                     return new ArrayList<UserInfo>();
                                                                                 } };

 
2.   
将查询出来的东西放到threadLocal,下次查询的时候,就可以先到threadLocal中取,如果没有,再调用远程服务

        UserInfo userInfo = getFromLocalByCardNo(cardNo);
        if (userInfo == null) {
            userInfo = userInfoQueryService.findUserInfo(cardNo);
            putToLocal(userInfo);
        }
 
3
.用户的请求结束,把这些内容清理掉。
为什么要把内容清理掉
如果一直存放在threadLocal

1是比较耗费内存;

2是里面的内容可能是过期的(用户修改了信息,threadLocal里面没有更新)

3请求结束后需要清除ThreadLocal的一个很重要的原因就是,如果使用了线程池,在请求结束后,线程的生命周期还没有结束,而是放回到池中,这样下次再使用此线程的时候就会获得上次的上下文了。

我们如何做到这一点呢?
开始之前先给大家介绍一个接口,spring mvc中的。HandlerInterceptor

这个接口中有三个方法
preHandle
在一个该方法会在Controller的方法执行前会被调用,可以使用这个方法来中断或者继续执行链的处理,当返回true时,处理执行链会继续,当返回false时,则不会去执行Controller的方法
postHandle
3个方法会在在controller的方法执行之后,在DispatcherServlet类导向到view进行render之前依次执行。最有用的是使 用postHandleRender方法,因为它有ModelAndView 传进来,那么我们就可以在render view之前往view中添加额外的model对象,或者对view的去处进行修改(例如下面的HTML还是用Excel来作为View ”例子就是对view进行了更改)

afterCompletion
该方法会在render view完成后执行,也可以说在请求过程(request processing)完成之后执行。该方法可以用来清理资源(例如象blackboard building block release context)

我们只需要在afterCompletion中把本次线程中的存放的信息清理掉,就可以了。
大家应该记得这一段代码,初始化threadLocal并不需要这一点,

     static {
        ThreadLocalCleaner.register(localUserInfo);
        ThreadLocalCleaner.register(localCertifyStatus);
    }

让我们先看下ThreadLocalCleaner的代码

    /**
     *
清理所有的线程变量。
     */
    public static void clear() {
        for (ThreadLocal<?> tl : tls) {
            tl.remove();
        }
    }

    /**
     * @param tl
     */
    public static void register(ThreadLocal<?> tl) {
        tls.add(tl);
    }
 
方 法register将初始化的ThreadLocal放到一个总的ThreadLocal里,方法clear将总的ThreadLocal里面的内容清 空。我们只需要在HandlerInterceptor实现类中的afterCompletion方法中把clear调用一下就可以了。

 


只有注册用户登录后才能发表评论。


网站导航: