线程池、ThreadLocal、Tomcat 应用小结

技术总结 yangchenhui 1196℃ 0评论

这两天遇到几个多线程的问题,多方找资料测试,现记录下测试结果,供自己以后查询用

问题描述:

项目最近在考虑Rest化,第一个问题就是session管理,现考虑使用redis存储session,并对不需要创建session的接口调用中,使用threadLocal存储用户信息,在请求开始时,将用户信息存储到threadLocal中,第一个问题,user信息会乱掉。

解决过程如下:
  1. 经调试发现,有时候在设置用户信息之前ThreadLocal中就已经存在了值。

  2. 然后继续调试,打印出线程号,发现多次请求线程号是相同的,也就是说,N次请求是有可能是使用同一个线程。

  3. 然后考虑到Tomcat等web容器肯定是有线程池机制的,可能是线程池在搞鬼,查看ThreadLocal源码资料,ThreadLocal最终是绑定在当前的Thread上的,而使用线程池之后,线程执行结束,并不会销毁,而是放回线程池中,这样下次请求时,直接使用了池中的线程,节约线程创建和销毁的资源开销。这样也确实能解释我遇到的问题。

  4. 当然一切只是猜测,而且自己使用线程池去验证下,代码如下:

创建一个对象,用来测试线程的局部变量:

package com.ych.test;  

public class TestObj {  

    private int i;  

    public TestObj(int i){  
        this.i = i;  
    }  

    public void setI(int i) {  
        this.i = i;  
    }  

    public int getI() {  
        return i;  
    }  
}  

创建线程,打印线程中的信息:

public class SaveThread extends Thread{  

    private TestObj obj;  

    private static ThreadLocal<TestObj> context = new ThreadLocal<TestObj>();  

    public SaveThread(TestObj obj,String name){  
        super(name);  
        this.obj = obj;  
    }  

    @Override  
    public void run() {  
        try{  
            if(context.get() == null){  
                System.out.println("threadLocal中为null");  
            }else{  
                System.out.println("threadLocal中为:"+context.get().getI());  
            }  
            context.set(obj);  
            System.out.println("thread name is " + Thread.currentThread().getName() + " id为:"+Thread.currentThread().getId()+",变量值:" +obj.getI());  
            Thread.sleep(2000);  
        }catch(InterruptedException ex){  
            ex.printStackTrace();  
        }  
    }  
}  

main方法中执行,查看执行结果:

public class TestThread {  

    private static Executor executor = Executors.newFixedThreadPool(2);  
    public static void main(String[] args) {  
        TestObj t1 = new TestObj(1);  
        TestObj t2 = new TestObj(2);  
        TestObj t3 = new TestObj(3);  

        SaveThread s1 = new SaveThread(t1,"11");  
        SaveThread s2 = new SaveThread(t2,"22");  
        SaveThread s3 = new SaveThread(t3,"33");  
        executor.execute(s1);  
        executor.execute(s2);  
        executor.execute(s3);  

    }  
}  

测试结果:

threadLocal中为null  
threadLocal中为null  
thread name is pool-1-thread-2 id为:16,变量值:2  
thread name is pool-1-thread-1 id为:15,变量值:1  
threadLocal中为:1  
thread name is pool-1-thread-1 id为:15,变量值:3  

看来我的推测基本是正确的。

最后,解决办法很简单,在spring拦截器中postHandle中remove掉该信息即可。

注:现在还有一个问题困扰我,如果线程不被销毁,那其中的变量岂不是不会被GC回收?如果线程较多的话是否会产生内存泄漏,这个还需要其他方法验证

本文固定链接:杨晨辉的个人博客 » 线程池、ThreadLocal、Tomcat 应用小结
本站内容除特别标注外均为原创,欢迎转载,但请保留出处!

喜欢 (1)
发表我的评论
取消评论
表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址