浅谈ThreadLocal
来源:互联网 发布:javascript表单提交 编辑:程序博客网 时间:2024/05/23 19:53
今天将探讨下JDK源码中出现频次很高的关键字–ThreadLocal,线程本地变量。
基本概念
ThreadLocal类,究竟是干啥的,首先来看java官方文档的说明:
该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。
简单的说,每个线程都有同一个变量的独立副本。
原理及用法
先看实例:
public class TestThreadLocal { static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(); public static void main(String[] args) throws InterruptedException { Thread child1 = new Thread() { @Override public void run() { System.out.println("child1 thread initial: " + threadLocal.get()); threadLocal.set(200); System.out.println("child1 thread final: " + threadLocal.get()); } }; Thread child2 = new Thread() { @Override public void run() { System.out.println("child2 thread initial: " + threadLocal.get()); threadLocal.set(300); System.out.println("child2 thread final: " + threadLocal.get()); } }; threadLocal.set(100); child1.start(); child2.start(); child1.join(); child2.join(); System.out.println("main thread final: " + threadLocal.get()); }}
运行结果:
child1 thread initial: nullchild1 thread final: 200child2 thread initial: nullchild2 thread final: 300main thread final: 100
从这个实例中可以看到ThreadLocal类的神奇之处,main线程对threadLocal变量的设置对child线程无影响,child线程对threadLocal变量改变也不会作用至main线程,他们虽然访问的同一个变量,但是每个线程有自身独立的变量副本。
来看ThreadLocal的主要方法:
ThreadLocal()就不用说了,无参的构造方法。
来看get()方法,获取值
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } return setInitialValue(); }ThreadLocalMap getMap(Thread t) { return t.threadLocals;}
从源码可以看出,根据当前线程获取到一个map,然后以当前ThreadLocal为key查询对应的value,如果value等于null,返回默认设置的value。每个线程都有一个Map,类型为ThreadLocalMap,调用set实际上是在线程自己的Map里设置了一个条目,键为当前的ThreadLocal对象,值为value。ThreadLocalMap是一个内部类,是专门用于ThreadLocal的,与一般的Map不同,它的键类型为WeakReference 弱引用类型,使用弱引用类型便于垃圾回收。
继续看setInitialValue()方法
private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; }protected T initialValue() { return null;}
可以看到,value是从initialValue方法获取然后添加至map中,然而initialValue返回null,这是咋回事?值得注意的是initialValue方法是protected修饰的,说明此方法就是为方便子类覆盖设计的,value需要在子类中initialValue方法设定。
再看set方法
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
没什么好说的,和setInitialValue方法差不多。
接着remove()
public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); }
从map中移除该线程键值。
每个线程都有一个Map,对于每个ThreadLocal对象,调用其get/set实际上就是以ThreadLocal对象为键读写当前线程的Map,就这样实现了每个线程都有自己的独立副本。
应用场景
1、多线程操作DateFormat/SimpleDateFormat时间日期类
对于DateFormat/SimpleDateFormat这类时间日期类,是非线程安全的,实现安全的方式之一是加锁,但更好的方式是使用ThreadLocal,每个线程用自己的变量副本。来看代码:
static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>(){protected DateFormat initialValue(){ return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); }};public static String dateToString(Date date){ return threadLocal.get().format(date);}
2、上下文信息
ThreadLocal的典型用途是提供上下文信息,比如在一个Web服务器中,一个线程执行用户的请求,在执行过程中,很多代码都会访问一些共同的信息,比如请求信息、用户身份信息、数据库连接、当前事务等,它们是线程执行过程中的全局信息,如果作为参数在不同代码间传递,代码会很啰嗦,这时,使用ThreadLocal就很方便,所以它被用于各种框架如Spring中,我们看个简单的示例:
public class RequestContext { public static class Request { //... }; private static ThreadLocal<String> localUserId = new ThreadLocal<>(); private static ThreadLocal<Request> localRequest = new ThreadLocal<>(); public static String getCurrentUserId() { return localUserId.get(); } public static void setCurrentUserId(String userId) { localUserId.set(userId); } public static Request getCurrentRequest() { return localRequest.get(); } public static void setCurrentRequest(Request request) { localRequest.set(request); }}
在首次获取到信息时,调用set方法如setCurrentRequest/setCurrentUserId进行设置,然后就可以在代码的任意其他地方调用get相关方法进行获取了。
最后,由于ThreadLocal的里ThreadLocalMap是弱引用类型,所以在线程池中使用时需要注意清除引用,具体可阅读《(82) 理解ThreadLocal / 计算机程序的思维逻辑》。
总结
1、ThreadLocal是使每个线程都有自己的变量副本,每个线程都有自己的map,是以ThreadLocal对象作为键存储数据的。
2、ThreadLocal经常用于存储上下文信息,简化代码。
- ThreadLocal浅谈
- ThreadLocal()浅谈
- 浅谈ThreadLocal
- 浅谈ThreadLocal
- 浅谈ThreadLocal
- ThreadLocal浅谈
- 浅谈ThreadLocal
- 浅谈ThreadLocal
- 浅谈ThreadLocal(转载)
- 浅谈ThreadLocal类
- 浅谈java.lang.ThreadLocal类
- 浅谈Spring声明式事务管理ThreadLocal和JDKProxy
- 浅谈对Java中ThreadLocal类的理解
- ThreadLocal
- ThreadLocal
- ThreadLocal
- ThreadLocal
- ThreadLocal
- MFC:创建dll和调用dll
- SuperMap iServer SDK 博客集锦
- 3des加密(双倍长)(ECB,CBC)详细介绍
- 运行android程序控制台输出异常The connection to adb is down, and a severe error has occured
- C#DES加密解密
- 浅谈ThreadLocal
- CodeForces
- 赫夫曼树与赫夫曼编码
- 我有一个秘密
- Linux初级学习第十二单元
- [Java]Stack栈和Heap堆的区别
- vue打包之后静态资源图片失效问题
- 安卓开发 :实现打开一次相机连续拍多张照片返回后拿到多张照片,一个小功能的构思
- CDH集群报一个DataNode未连接上的错误