博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ThreadLocal应用场景分析
阅读量:2165 次
发布时间:2019-05-01

本文共 6424 字,大约阅读时间需要 21 分钟。

ThreadLocal

  • 为解决多线程并发问题提供一种新思路。ThreadLocal并不是一个Thread,而是Thread局部变量

ThreadLocal作用

  • 解决多线程环境下整个上下文调用需要将关键参数透传
  • 如果不使用ThreadLocal,每个方法都要加关键参数,如果内部方法链路过长,那么代码看起来冗余、臃肿
  • 如果某处传时将参数值改掉或设置为null,后续调用方法中用到这个参数的代码会受到影响
  • 未用ThreadLocal的例子,模拟HTTP服务器接收用户请求
    package com.myd.cn.ThreadLocal;	import java.util.Collections;	import java.util.List;	import java.util.concurrent.ExecutorService;	import java.util.concurrent.Executors;	public class NoThreadLocalHTTPServer {		public static void main(String[] args) {			simpleTest();		}		public static void simpleTest(){			ExecutorService executorService = Executors.newFixedThreadPool(2);			//提交线程任务			executorService.submit(()->{				Long userId = 10000L;				doUserRequest(userId);			});			executorService.submit(()->{				Long userId = 20000L;				doUserRequest(userId);			});		}		private static void doUserRequest(Long userId) {			ThreadContext threadContext = new ThreadContext();			threadContext.setUserId(userId);			String info = getCusInfo(threadContext);			List
    courses = getMyCourse(threadContext); System.out.println("user info "+info +";courses = "+courses.toString()); } private static String getCusInfo(ThreadContext threadContext){ return threadContext.getUserId() +" 的UserId信息"; } private static List
    getMyCourse(ThreadContext threadContext){ return Collections.singletonList(threadContext.getUserId()+" 相应的课程"); } public static class ThreadContext{ private Long userId; public Long getUserId() { return userId; } public void setUserId(Long userId) { this.userId = userId; } } }
  • 使用ThreadLocal例子
    package com.myd.cn.ThreadLocal;	import java.util.Collections;	import java.util.List;	import java.util.concurrent.ExecutorService;	import java.util.concurrent.Executors;	public class ThreadLocalHTTPServer {		public static void main(String[] args) {			simpleTest();		}		private static void simpleTest() {			ExecutorService executorService = Executors.newFixedThreadPool(2);			executorService.submit(()->{				 Long userId = 1000L;				 doUseRequeset(userId);			});			executorService.submit(()->{				 Long userId = 2000L;				 doUseRequeset(userId);			});		}				private static void doUseRequeset(Long userId) {			UserSessionContext.getUserSessionContext().setUserId(userId);			String info = getCusInfo();			List
    courses = getCourse(); System.out.println("user info "+info +";courses = "+courses.toString()); } public static String getCusInfo(){ return UserSessionContext.getUserSessionContext().getUserId()+" 的相关信息"; } public static List
    getCourse(){ return Collections.singletonList(UserSessionContext.getUserSessionContext().getUserId()+"的 相关课程信息"); } private static class UserSessionContext{ private static ThreadLocal
    threadLocal = ThreadLocal.withInitial(UserSessionContext::new); //获取ThreadLocal包含的(线程本地存储)的变量UserSessionContext public static UserSessionContext getUserSessionContext(){ return threadLocal.get(); } /** * 删除本地存储的变量,线程执行完毕时删除,避免内存溢出 */ public static void removeContext(){ threadLocal.remove(); } private Long userId; public static ThreadLocal
    getThreadLocal() { return threadLocal; } public static void setThreadLocal(ThreadLocal
    threadLocal) { UserSessionContext.threadLocal = threadLocal; } public Long getUserId() { return userId; } public void setUserId(Long userId) { System.out.println(Thread.currentThread().getName()+ " set userId = "+userId); this.userId = userId; } } }

ThreadLocal原理

  • java四种引用方式
    四种引用方式
  • 弱引用例子:对象被弱引用后置空,JVM触发GC会将其回收
     

1.代码

package com.myd.cn.ThreadLocal;

import java.lang.ref.WeakReference;		/**		 * 测试弱引用,弱引用的对象并没有被其他对象直接引用,		 * 那么对于ThreadLocal->threadlocals->threadLlocalMap->key的弱引用被回收,相应强引用的value则不会被回收,造成OOM		 * @author dymll		 *		 */		public class WeakReferenceOfThreadLocal {			public static void main(String[] args) throws Exception {				testWeakReferenceNoDirectReference() ;			}			public static void testWeakReferenceNoDirectReference() throws Exception{				Object obj = new Object();				WeakReference weakReference = new WeakReference(obj);				System.out.println(weakReference.get());				obj = null;				//将obj变量置空 ,触发手动GC,后续引用为空				System.gc();				System.out.println(weakReference.get());			}		}

2.结果,查看被list对象引用obj仍有输出

java.lang.Object@15db9742
null
````

  • 如果对象被弱引用,且其后又被其他对象直接引用(强引用),则后面即使被置空,也不会被触发的GC操作回收掉
    1.代码		package com.myd.cn.ThreadLocal;		import java.lang.ref.WeakReference;		import java.util.ArrayList;		import java.util.List;		/**		 * 测试弱引用,弱引用的对象并没有被其他对象直接引用,		 * 那么对于ThreadLocal->threadlocals->threadLlocalMap->key的弱引用被回收,相应强引用的value则不会被回收,造成OOM		 * @author dymll		 *		 */		public class StrongReferenceAfterWeakReference {			public static void main(String[] args) throws Exception {				testWeakReferenceNoDirectReference() ;			}			public static void testWeakReferenceNoDirectReference() throws Exception{				Object obj = new Object();				WeakReference weakReference = new WeakReference(obj);				System.out.println(weakReference.get());				List objects = new ArrayList<>();				//若在弱引用之后又有直接引用(强引用),则及时被置空,也不会被GC				objects.add(obj);				obj = null;				//将obj变量置空 ,触发手动GC,后续引用为空				System.gc();				System.out.println(weakReference.get());			}		}

2.结果,查看被list对象引用obj仍有输出

java.lang.Object@15db9742
java.lang.Object@15db9742

## ThreadLocal数据结构- 查看其源码,ThreadLocal数据存在其下Threadlocals的ThreadLocalMap,Map有Entry构成,Entry包含每个线程作为key,value是每个线程专有的数据(这里是变量)	![ThreadLocal数据结构](https://img-blog.csdnimg.cn/20200327000458779.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2R5bWtrag==,size_16,color_FFFFFF,t_70)- 每个线程都有自己的ThreadLocal	![每个线程都有自己ThreadLocal](https://img-blog.csdnimg.cn/20200327000515974.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2R5bWtrag==,size_16,color_FFFFFF,t_70)## ThreadLocal OOM问题- key是软引用,被置空后会被回收,那么Thread->ThreadLocalMap->Entry->Value这个链路是强引用链路,ThreadLocalMap无法再通过key来访问value,value在这里不会被回收,当这样的对象过多占用内存是,发生OOM	![强引用且无法进行回收,而不能进行内存释放,造成内存泄漏](https://img-blog.csdnimg.cn/20200327000544676.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2R5bWtrag==,size_16,color_FFFFFF,t_70)- 如果解决ThreadLocal OOM问题- 在使用ThreadLocal 时,都要在线程全部指向完毕后,在finally代码块中调用remove()方法,清除内存(线程池中尤为注意要清除内存,更易因为强引用无法回收内存,造成OOM)- 保存在ThreadLocal的数据不能过大

转载地址:http://ihjzb.baihongyu.com/

你可能感兴趣的文章
(PAT 1073) Scientific Notation (字符串模拟题)
查看>>
(PAT 1080) Graduate Admission (排序)
查看>>
Play on Words UVA - 10129 (欧拉路径)
查看>>
mininet+floodlight搭建sdn环境并创建简答topo
查看>>
【linux】nohup和&的作用
查看>>
Set、WeakSet、Map以及WeakMap结构基本知识点
查看>>
【NLP学习笔记】(一)Gensim基本使用方法
查看>>
【NLP学习笔记】(二)gensim使用之Topics and Transformations
查看>>
【深度学习】LSTM的架构及公式
查看>>
【python】re模块常用方法
查看>>
剑指offer 19.二叉树的镜像
查看>>
剑指offer 20.顺时针打印矩阵
查看>>
剑指offer 21.包含min函数的栈
查看>>
剑指offer 23.从上往下打印二叉树
查看>>
剑指offer 25.二叉树中和为某一值的路径
查看>>
剑指offer 60. 不用加减乘除做加法
查看>>
Leetcode C++《热题 Hot 100-14》283.移动零
查看>>
Leetcode C++《热题 Hot 100-15》437.路径总和III
查看>>
Leetcode C++《热题 Hot 100-17》461.汉明距离
查看>>
Leetcode C++《热题 Hot 100-18》538.把二叉搜索树转换为累加树
查看>>