在线程池环境下使用ThreadLocal与内存池露
线程池会保留一部分空闲线程仍然存活。如果处理ThreadLocal变量的线程是由线程池维护的,在某些情况下(例如ThreadLocal<T>的T是个大对象),有必要使用ThreadLocal.remove()手工清除ThreadLocal中的内容以释放其占用的内存空间。
Tomcat6使用NIO后对ThreadLocal的影响
很早就听说tomcat6使用nio了,这几天突然想到一个问题,使用nio代替传统的bio,ThreadLocal岂不是会存在冲突?
首先,何谓nio?
如果读者有socket的编程基础,应该会接触过堵塞socket和非堵塞socket,堵塞socket就是在accept、read、write等IO操作的的时候,如果没有可用符合条件的资源,不马上返回,一直等待直到有资源为止。而非堵塞socket则是在执行select的时候,当没有资源的时候堵塞,当有符合资源的时候,返回一个信号,然后程序就可以执行accept、read、write等操作,这个时候,这些操作是马上完成,并且马上返回。而windows的winsock则有所不同,可以绑定到一个EventHandle里,也可以绑定到一个HWND里,当有资源到达时,发出事件,这时执行的io操作也是马上完成、马上返回的。一般来说,如果使用堵塞socket,通常我们时开一个线程accept socket,当有socket链接的时候,开一个单独的线程处理这个socket;如果使用非堵塞socket,通常是只有一个线程,一开始是select状态,当有信号的时候马上处理,然后继续select状态。
按照大多数人的说法,堵塞socket比非堵塞socket的性能要好。不过也有小部分人并不是这样认为的,例如Indy项目(Delphi一个比较出色的网络包),它就是使用多线程+堵塞socket模式的。另外,堵塞socket比非堵塞socket容易理解,符合一般人的思维,编程相对比较容易。
nio其实也是类似上面的情况。在JDK1.4,sun公司大范围提升Java的性能,其中NIO就是其中一项。Java的IO操作集中在java.io这个包中,是基于流的阻塞API(即BIO,Block IO)。对于大多数应用来说,这样的API使用很方便,然而,一些对性能要求较高的应用,尤其是服务端应用,往往需要一个更为有效的方式来处理IO。从JDK 1.4起,NIO API作为一个基于缓冲区,并能提供非阻塞O操作的API(即NIO,non-blocking IO)被引入。
BIO与NIO一个比较重要的不同,是我们使用BIO的时候往往会引入多线程,每个连接一个单独的线程;而NIO则是使用单线程或者只使用少量的多线程,每个连接共用一个线程。
这个时候,问题就出来了:我们非常多的java应用是使用ThreadLocal的,例如JSF的FaceContext、Hibernate的session管理、Struts2的Context的管理等等,几乎所有框架都或多或少地应用ThreadLocal。如果存在冲突,那岂不惊天动地?
后来终于在Tomcat6的文档(http://tomcat.apache.org/tomcat-6.0-doc/aio.html)找到答案。根据上面说明,应该Tomcat6应用nio只是用在处理发送、接收信息的时候用到,也就是说,tomcat6还是传统的多线程Servlet,我画了下面两个图来列出区别:
tomcat5:客户端连接到达 -> 传统的SeverSocket.accept接收连接 -> 从线程池取出一个线程 -> 在该线程读取文本并且解析HTTP协议 -> 在该线程生成ServletRequest、ServletResponse,取出请求的Servlet -> 在该线程执行这个Servlet -> 在该线程把ServletResponse的内容发送到客户端连接 -> 关闭连接。
我以前理解的使用nio后的tomcat6:客户端连接到达 -> nio接收连接 -> nio使用轮询方式读取文本并且解析HTTP协议(单线程) -> 生成ServletRequest、ServletResponse,取出请求的Servlet -> 直接在本线程执行这个Servlet -> 把ServletResponse的内容发送到客户端连接 -> 关闭连接。
实际的tomcat6:客户端连接到达 -> nio接收连接 -> nio使用轮询方式读取文本并且解析HTTP协议(单线程) -> 生成ServletRequest、ServletResponse,取出请求的Servlet -> 从线程池取出线程,并在该线程执行这个Servlet -> 把ServletResponse的内容发送到客户端连接 -> 关闭连接。
从上图可以看出,BIO与NIO的不同,也导致进入客户端处理线程的时刻有所不同:tomcat5在接受连接后马上进入客户端线程,在客户端线程里解析HTTP协议,而tomcat6则是解析完HTTP协议后才进入多线程,另外,tomcat6也比5早脱离客户端线程的环境。
实际的tomcat6与我之前猜想的差别主要集中在如何处理servlet的问题上。实际上即使抛开ThreadLocal的问题,我之前理解tomcat6只使用一个线程处理的想法其实是行不同的。大家都有经验:servlet是基于BIO的,执行期间会存在堵塞的,例如读取文件、数据库操作等等。tomcat6使用了nio,但不可能要求servlet里面要使用nio,而一旦存在堵塞,效率自然会锐降。
所以,最终的结论当然是tomcat6的servlet里面,ThreadLocal照样可以使用,不存在冲突。
分享到:
相关推荐
Java的`ThreadLocal`类提供了一种机制,可以在每个线程中存储独立的副本,避免了多线程环境下的数据冲突。 8. **异步Servlet** 从Servlet 3.0开始,Servlet API提供了异步处理能力,允许Servlet在不阻塞线程的情况...
9. **Tomcat的线程局部变量(ThreadLocal)使用**:ThreadLocal可以帮助减少数据同步,但过度使用可能导致内存泄漏,需要谨慎。 10. **监控与日志**:使用JMX监控Tomcat运行状态,通过日志分析性能瓶颈,是优化并发...
- 在并发环境下,`HashMap`可能会出现死锁等问题,主要是因为其内部操作是非线程安全的。 - 解决方案通常包括使用`ConcurrentHashMap`或者对`HashMap`进行外部同步控制。 #### LinkedHashMap的应用 - `...
- 场景包括分布式环境下的资源互斥访问、防止并发操作导致的数据不一致等。 - 实现方案包括基于Redis的分布式锁、基于ZooKeeper的分布式锁等。 - **分布式事务的实现方案** - 传统的两阶段提交(2PC)和三阶段...
而`StringBuffer`和`StringBuilder`在多线程环境下,`StringBuffer`是线程安全的,`StringBuilder`在单线程下效率更高。 2. `ArrayList`和`LinkedList`的主要区别在于数据结构和访问效率:`ArrayList`基于数组,...
2. **分布式锁**:分布式锁是多节点环境下确保数据一致性的重要工具,常见实现包括基于Zookeeper、Redis或数据库的锁机制。 3. **Nginx 请求转发**:Nginx可以通过负载均衡策略将请求转发至不同服务器,其中按权重...
- **StringBuilder** 性能优于 `StringBuffer`,但在单线程环境下使用更佳。 2. **ArrayList和LinkedList的区别** - **ArrayList** 基于动态数组实现,支持随机访问元素,插入删除效率较低。 - **LinkedList** ...
- **StringBuffer/StringBuilder**:可变的字符序列,StringBuilder 在单线程环境中比 StringBuffer 性能更好。 #### 四、泛型 泛型允许你编写类型安全的通用代码。理解泛型接口和方法、类型擦除、协变、逆变等...
1. 目录 1. 2. 目录 .........................................................................................................................................................1 JVM ........................