概述
线程封闭是一种较为简单的线程并发的方法。它其实把对象封装到一个线程里,该对象只对该线程是可见的。当然也就是线程安全的了。
实现线程封闭的方法
- Ad-hoc 线程封闭:依赖程序控制实现,脆弱,是最糟糕的一种方式,不推荐!
- 堆栈封闭:应用广泛,依靠各线程局部变量的堆栈拷贝副本实现,无并发问题。避免使用全局变量。
- 数据库连接对应JDBC的Connection对象。
- ThreadLocal线程封闭:实现较好,效率较高。(以后会做源码分析……)
- 堆栈封闭:即指的是方法/类中的局部变量,默认是通过线程拷贝副本实现。
- Connection对象在实现中并未对线程安全做过多的处理,JDBC的规范中也未要求Connection对象必须是线程安全的。但实际服务器的应用程序中,线程从连接池获取Connection对象,只有在使用结束后才将其返回给连接池,期间其他线程是获取不到Connection对象的。该机制显式提供了线程封闭。
ThreadLocal测试例子
RequestHolder.java
1 | public class RequestHolder { |
方法分析:
- 该类存放需要绑定的信息。
- 其中add操作是在请求进入后端服务器,但还未进行实际处理时,调用该方法,写入相关信息。(通过filter:先拦截对应的URL,当前台访问该URL时,将相关信息写入ThreadLocal中;当URL实际被处理时,可直接从ThreadLocal中取出信息)。
- 定义移除方法,防止内存泄漏。在接口处理完之后进行处理(通过intercepter实现)。
HttpFilter.java
1 | 4j |
方法分析:
- 因为是通过http请求,ServletRequest需转换为HttpServletRequest类型。
- 在RequestHolder中放入URL相关信息。
- 最后若该filter不是想拦截住该请求,只是做相关的数据处理,还想让其他过滤器接收到,则需最后调用filterChain.doFilter(servletRequest, servletResponse)。
HttpInterceptor.java
1 | 4j |
ThreadLocalController.java
1 | @Controller |
ConcurrencyApplication.java
1 | true//使用springboot快速进行测试 |
方法分析:
- 通过springboot创建registrationBean并指定过滤URL类型为”/threadlocal/*”。
- 重写addInterceptors -> 添加拦截器,并指定拦截的路径类型。
- ThreadLocalController.java中指定请求映射的名称和返回内容。
接口测试结果:(使用Postman进行接口测试)
日志部分截图:
可以看出例子是和日志完全对应的。
重复一下threadlocal的实现思想:当一个请求进来时,通过过滤器Filter,将数据信息(这里是线程id)存储到threadlocal中,当接口被调用处理时,可以直接从中取出来;当接口处理完成,通过拦截器Interceptor的afterCompletion把当前线程中的数据信息(这里是线程id)移除,避免内存泄漏。