ThreadLocal探索一

通过前面一篇的学习笔记,对ThreadLocal只是有了一个基本的认识,ThreadLocal主要是为了解决变量/参数传递问题,似乎并不是为了解决共享变量访问的多线程访问问题。

之前我存在这样的错误认识:WEB应用中,对于每次HTTP请求,WEB容器都会为其创建一个新的线程并执行。

于是我在每次请求时,将当前线程的内存地址信息打印出来,测试发现,多次请求时,存在输出同一个地址的情况,这就证明了,Tomcat内部可能存在一种类似池的技术,按照规则将线程分配给我们的Http请求,显然这种分配不是我们所能掌控的,

可能用户A、用户B Http请求与相同的线程对应,也可能用户A2次请求分别与2个不同的线程对应,这个时候就要谨慎的使用ThreadLocal这个东东了。

还是结合上一篇文章来说说,上一篇文章的代码实现存在这样的问题:

多个用户的多个Http请求可能使用相同的线程,这样就不能区分1[线程局部变量mycontext]是属于哪个用户的哪次请求。

即:线程也线程之间是没有影响了,但http请求之间却存在影响。

所以需要对以前的代码做一下修改,思路大概是这样的:

每次Http请求时,在Servlet的最开始,就new一个新的mycontext,并将它跟当前线程关联。

这样每次Http请求时,相当于创建了一个新的线程局部变量,不仅线程与线程之间没有影响,而且请求与请求之间也不存在影响。

改造后的结果如下:

 1 public class MyContext
 2 {
 3     // 将List<String>的一个对象,作为线程的局部变量
 4     private static ThreadLocal<List<String>> mycontext = new ThreadLocal<List<String>>();
 5     …
 6     /**
 7      * 直接将str添加到与当前线程相关的context中
 8      */
 9     public static void add(String str)
10     {
11         if(str == null)
12             return;
13         mycontext.get().add(str);
14     }
15     …
16 }

 

 1 public class MyServlet
 2 {
 3     /**
 4      * 模拟Servlet,处理请求
 5      */
 6     public void execute()
 7     {
 8         // 在每次请求最开始时,创建一个新的context,并设置其与当前线程关联
 9         MyContext.set(new ArrayList<String>());
10         for(int i = 0; i < 3; i++)
11         {
12             sleep(10);
13             MyContext.add(Thread.currentThread().getName() + " - " + new Date().getTime());
14         }
15     }
16     …
17 }

Struts2中的ActionContext也是一个线程局部变量,我们可以在FilterDispather中看到,每次Http请求时,在都会创建一个新的ActionContext,并将它跟当前线程关联。

 

另,附上一个链接,介绍Tomcat线程池相关知识的,http://hi.baidu.com/xjll1314/blog/item/239885036d49f408728da565.html

其中关于Http请求时线程分配,有这样一段话:

Tomcat 的线程池位于tomcat-util.jar文件中,包含了两种线程池方案。方案一:使用APRPool技术,使用了JNI;方案二:使用Java实现的ThreadPool。这里介绍的是第二种。如果想了解APRPool技术,可以查看APR的源代码。

ThreadPool默认创建了5个线程,保存在一个200维的线程数组中,创建时就启动了这些线程,当然在没有请求时,它们都处理等待状态(其实就是一个while循环,不停的等待notify)。如果有请求时,空闲线程会被唤醒执行用户的请求。

具体的请求过程是: 服务启动时,创建一个一维线程数组(maxThread200个),并创建空闲线程(minSpareThreads5)随时等待用户请求。 当有用户请求时,调用 threadpool.runIt(ThreadPoolRunnable)方法,将一个需要执行的实例传给ThreadPool中。其中用户需要执行的实例必须实现ThreadPoolRunnable接口。 ThreadPool 首先查找空闲的线程,如果有则用它运行要执行ThreadPoolRunnable;如果没有空闲线程并且没有超过maxThreads,就一次性创建 minSpareThreads个空闲线程;如果已经超过了maxThreads了,就等待空闲线程了。总之,要找到空闲的线程,以便用它执行实例。找到后,将该线程从线程数组中移走。 接着唤醒已经找到的空闲线程,用它运行执行实例(ThreadPoolRunnable)。 运行完ThreadPoolRunnable后,就将该线程重新放到线程数组中,作为空闲线程供后续使用。

posted on 2010-01-09 00:25  TroyZ  阅读(358)  评论(0编辑  收藏  举报