这里先说下ThreadLocal不是一个线程的本地实现版本,不是一个Thread,它是thread local variable(线程局部变量);用于实现线程内的数据共享,即对于相同的程序代码,多个模块在同一个线程中运行时要共享一份数据,而在另外线程中运行时又共享另外一份数据。换一句话说就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量
现做一个小练习,定义一个全局共享的ThreadLocal变量,然后启动五个线程向该ThreadLocal变量中存储一个随机值,接着各个线程调用另外其他多个类的方法,这多个类的方法中读取这个ThreadLocal变量的值,就可以看到多个类在同一个线程中共享同一份数据。
定义两个类TestA 、TestB,用于获取值MyThreadLocal类中的X值
TestA:
- package com.study.threadlocal;
- /**
- *
- * @ClassName: TestA
- * @Description: 用于获取 MyThreadLocal 中变量x的值
- * @author 我夕
- */
- public class TestA {
- public void print(){
- System.out.println(Thread.currentThread()+": TestA ,x current value is:"+MyThreadLocal.getInstance().getX());
- }
- }
TestB:
- package com.study.threadlocal;
- /**
- *
- * @ClassName: TestB
- * @Description: 用于获取 MyThreadLocal 中变量x的值
- * @author 我夕
- */
- public class TestB {
- public void print(){
- System.out.println(Thread.currentThread()+": TestB ,x current value is:"+MyThreadLocal.getInstance().getX());
- }
- }
MyThreadLocal:
- package com.study.threadlocal;
- /**
- *
- * @ClassName: MyThreadLocal
- * @Description: TODO
- * @author 我夕
- */
- public class MyThreadLocal {
- private Integer x;
- //将构造器声明有私有的,避免外部创建他
- private MyThreadLocal(){}
- private static ThreadLocal instanceThreadLocal=new ThreadLocal();//new ThreadLocal
- public static MyThreadLocal getInstance(){
- MyThreadLocal instance=(MyThreadLocal)instanceThreadLocal.get();
- if(instance==null){
- instance=new MyThreadLocal();
- instanceThreadLocal.set(instance);
- }
- return instance;
- }
- public Integer getX() {
- return x;
- }
- public void setX(Integer x) {
- this.x = x;
- }
- //除去线程的方法
- public static void remove(){
- instanceThreadLocal.remove();
- }
- }
ThreadLocalTest
- package com.study.threadlocal;
- import java.util.Random;
- /**
- *
- * @ClassName: ThreadLocalTest
- * @Description: 线程局部变量练习
- * @author 我夕
- */
- public class ThreadLocalTest {
- public static void main(String[] args) {
- final TestA testA=new TestA();
- final TestB testB=new TestB();
- //创建5个线程
- for(int i=0;i<5;i++){
- Thread thread=new Thread(new Runnable() {
- @Override
- public void run() {
- //生成一个随机数字给x
- MyThreadLocal.getInstance().setX(new Random().nextInt(100));
- System.out.println(Thread.currentThread()+",X current value is:"+MyThreadLocal.getInstance().getX());
- testA.print();
- testB.print();
- MyThreadLocal.getInstance().remove();
- }
- });
- thread.setName("thread_"+i);
- thread.start();
- }
- }
- }
运行
从以上可以看出,TestA 与TestB确实在同一个线程中共享同一份数据。
接下来,分析下 ThreadLocal set()与get()方法源码
set源码:
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
get源码
- /**
- * Returns the value in the current thread's copy of this
- * thread-local variable. If the variable has no value for the
- * current thread, it is first initialized to the value returned
- * by an invocation of the {@link #initialValue} method.
- *
- * @return the current thread's value of this thread-local
- */
- 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();
- }
set()、get()方法,我想大家应该都知道其的作用了,set设置当前线程的线程局部变量副本的值,get返回当前线程的线程局部变量副本的值,其set方法相当于往其内部的map中增加一条记录,key分别是各自的线程,value是各自的set方法传进去的值,在线程结束时可以调用ThreadLocal.clear()方法,这样会更快释放内存,不调用也可以,因为线程结束后也可以自动释放相关的ThreadLocal变量。