ThreadLocal的深入理解及应用

是什么

ThreadLocal很容易让人望文生义,想当然地认为是一个“本地线程”。其实,ThreadLocal并不是一个Thread , 它类似(Map),用来存储当前运行线程及对应的变量。

在WEB应用中每次Http请求,都相当于从线程池取一个空闲线程对请求的方法作处理。此时当前线程的所有方法中Thread.currentThread()都是一样,然后我们可以把它假想为Map的key,value可以存一个在整个请求中都用到的变量。

好处

1:避免了传参

例如在dao层 userDao下有_save(User user)_,update(Department department)两种方法,

service中accessionService的_accession_方法

boolean accssion(User user, Department department, Connection conn) {

conn.begin();

save(user);

update(department);

conn.commint();

}

为了保证数据一致性,必须在service层添加事务,那么每有一个新的连接(请求),必须要传一个conn参数,做法不好,污染api。

2:解决了高并发中对资源的争夺(牺牲空间换取时间与synchronize相反)。

ThreadLocal源码如下

1 //一般我们用ThreadLocal,通过调用get(),对其初始化 2 public T get() { 3 Thread t = Thread.currentThread(); //获取当前线程 4 ThreadLocalMap map = getMap(t); 5 if (map != null) { 6 ThreadLocalMap.Entry e = map.getEntry(this); 7 if (e != null) { 8 @SuppressWarnings("unchecked") 9 T result = (T)e.value; 10 return result; 11 } 12 } 13 return setInitialValue(); //初始化ThreadLocalMap 14 } 15 16 private T setInitialValue() { 17 T value = initialValue(); 18 Thread t = Thread.currentThread(); 19 ThreadLocalMap map = getMap(t); 20 if (map != null) 21 map.set(this, value); 22 else 23 createMap(t, value); 24 return value; 25 } 26 27 //ThreadLocalMap其实是一个entry(静态内部类)继承了WeakReference,是一个弱关联,并非数组 28 ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { 29 table = new Entry[INITIAL_CAPACITY]; 30 int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); 31 table[i] = new Entry(firstKey, firstValue); 32 size = 1; 33 setThreshold(INITIAL_CAPACITY); 34 } 35 36 //改变当前线程绑定值 37 public void set(T value) { 38 Thread t = Thread.currentThread(); 39 ThreadLocalMap map = getMap(t); 40 if (map != null) 41 map.set(this, value); 42 else 43 createMap(t, value); 44 } 45 46 void createMap(Thread t, T firstValue) { 47 t.threadLocals = new ThreadLocalMap(this, firstValue); 48 } 49 50 //map.set()具体实现,根据当前线程key定位到entry[i],再改变entry[i]对应的值为value 51 private void set(ThreadLocal<?> key, Object value) { 52 53 Entry[] tab = table; 54 int len = tab.length; 55 int i = key.threadLocalHashCode & (len-1); 56 57 for (Entry e = tab[i]; 58 e != null; 59 e = tab[i = nextIndex(i, len)]) { 60 ThreadLocal<?> k = e.get(); 61 62 if (k == key) { 63 e.value = value; 64 return; 65 } 66 67 if (k == null) { 68 replaceStaleEntry(key, value, i); 69 return; 70 } 71 } 72 73 tab[i] = new Entry(key, value); 74 int sz = ++size; 75 if (!cleanSomeSlots(i, sz) && sz >= threshold) 76 rehash(); 77 }

应用

在spring切面方法中,我们没有办法直接从ProceedingJoinPoint中获取request和response对象,但是我们要对request的传递参数处理,同时也要用response添加cookie,实现重定向等功能。

1//过滤器,每次请求过来都经过这个过滤器处理 2public class GetContent implements Filter { 3 @Override 4 public void destroy() { 5 // TODO Auto-generated method stub 6 } 7 @Override 8 public void doFilter(ServletRequest arg0, ServletResponse arg1, 9 FilterChain arg2) throws IOException, ServletException { 10 SysContent.setRequest((HttpServletRequest) arg0); 11 SysContent.setResponse((HttpServletResponse) arg1); 12 arg2.doFilter(arg0, arg1); 13 } 14 @Override 15 public void init(FilterConfig arg0) throws ServletException { 16 // TODO Auto-generated method stub 17 } 18} 19 20//把response、request与当前线程绑定,具体实现 21public class SysContent { 22 private static ThreadLocal<HttpServletRequest> requestLocal= new ThreadLocal<HttpServletRequest>(); 23 private static ThreadLocal<HttpServletResponse> responseLocal= new ThreadLocal<HttpServletResponse>(); 24 25 public static HttpServletRequest getRequest() { 26 return (HttpServletRequest)requestLocal.get(); 27 } 28 public static void setRequest(HttpServletRequest request) { 29 requestLocal.set(request); 30 } 31 public static HttpServletResponse getResponse() { 32 return (HttpServletResponse)responseLocal.get(); 33 } 34 public static void setResponse(HttpServletResponse response) { 35 responseLocal.set(response); 36 } 37 public static HttpSession getSession() { 38 return (HttpSession)((HttpServletRequest)requestLocal.get()).getSession(); 39 } 40} 41 42@Component 43@Aspect 44public class ActionShareAspect { 45 private static final Logger logger = LoggerFactory.getLogger(ActionLogAspect.class); 46 47 //配置切入点 48 @Pointcut("execution(public * com.xx.xx.*.*(..))") // 配置切入点 49 public void pointcut(){} 50 51 @Around("pointcut()") 52 public Object aroundMethod(ProceedingJoinPoint pjd) { 53 //request、response获取 54 HttpServletRequest request = SysContent.getRequest(); 55 HttpServletResponse response = SysContent.getResponse(); 56 57 //执行目标方法 58 String methodName = pjd.getSignature().getName(); 59 Object result = null; 60 try { 61 //...略 62 //返回通知 63 result = pjd.proceed(); 64 return result; 65 } catch (Exception e) { 66 //... 67 } 68 }

回顾来思考最初的问题:关于spring的事务。我们可以把每次请求从数据连接池获取的connection绑定到ThreadLocal上,那么就解决了service中事务的问题。当然这里要对service进行切面编程,使用代理,下次博客再细说。

点赞
收藏

评论区

加载中...

相关推荐

spring中,在Java任何位置获取request对象

看RequestContextListener和RequestContextHolder源代码不难看出,是用过ThreadLocal实现的。ThreadLocal是什么呢?看词义,它并非是一个线程,它不是一个Thread,而是一个线程局部变量,其实功能非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而

java ThreadLocal

ThreadLocal是什么定义:提供线程局部变量;一个线程局部变量在多个线程中,分别有独立的值(副本)特点:简单(开箱即用)、快速(无额外开销)、安全(线程安全)场景:多线程场景(资源持有、线程一致性、并发计算、线程安全等场景)ThreadLocal基本API 构

ThreadLocal源码解析及实战应用

ThreadLocal是一个关于创建线程局部变量的类。通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。而使用ThreadLocal创建的变量只能被当前线程访问,其他线程则无法访问和修改。ThreadLocal在设计之初就是为解决并发问题而提供一种方案,每个线程维护一份自己的数据,达到线程隔离的效果。

Java ThreadLocal的内存泄漏问题

ThreadLocal提供了线程独有的局部变量,可以在整个线程存活的过程中随时取用,极大地方便了一些逻辑的实现。常见的ThreadLocal用法有:\存储单个线程上下文信息。比如存储id等;\使变量线程安全。变量既然成为了每个线程内部的局部变量,自然就不会存在并发问题了;\减少参数传递。比如做一个trace工具,能够输出工程从开始到结

Java多线程与并发之ThreadLocal原理解析

1\.ThreadLocal是什么?使用场景ThreadLocal简介ThreadLocal是线程本地变量,可以为多线程的并发问题提供一种解决方式,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,

Java并发编程的艺术笔记(四)——ThreadLocal的使用

ThreadLocal,即线程变量,是一个以ThreadLocal对象为键、任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。目的就是为了让线程能够有自己的变量可以通过set(T)方法来设置一个值,在当前线程下再通过get()方法获取到原先设置的值