(基础)如何使用ThreadLocal

ThreadLocal的作用:

使同一个线程共享一个实例,不同线程使用各自的实例,互不影响

 

ThreadLocal比较正宗的应用解释:

每个线程都将拥有一份属于自己的变量,只要Thread没结束,随时都能取出其内部的ThreadLocalMap,然后结合ThreadLocal自身,就可以拿到Map中存放的变量。

为什么要这样?解决了方法链中传递同一个参数的麻烦!

 

应用场景:

一个线程在运行,需要执行很多类的不同方法,这些方法都使用到了一个变量

那么就得不断的把这个变量往后传递,很麻烦

如何在Thread的整个生命周期中都能使用这个变量呢,使用ThreadLocalMap来保存。

 

每个Thread中都有一个属性变量:

ThreadLocal.ThreadLocalMap    threadLocals

ThreadLocal中定义了一个内部类(当做一个Map):

ThreadLocalMap map

这个Map比较特殊:

key    -   ThreadLocal 

value -   我们要传递的变量

 

 

Thread类中的这个Map是怎么初始化的呢?

创建ThreadLocal对象,当调用其get方法获取变量时,如果从Thread中取到的Map为空,

则创建一个新的Map,并会将引用赋值给Thread中的那个threadLocals

这样,Thead中就持有了一份Map的引用

任何时候只要该线程还在运行,都可以通过currentThread得到它自己的ThreadLocalMap

然后,以ThreadLocal为key,即可从Map中取出其中存放的变量、

 

在设计上,对于开发人员来讲,Thread和ThreadLocalMap都隐藏在了背后

而是通过直接操作ThreadLocal来完成变量的存入,获取,删除操作的!

最好要理解背后隐藏的一些东西,这样才能深刻理解运行原理。 

 

源码:

Thread类持有ThreadLocal的引用

public class Thread implements Runnable {
        ...
        ThreadLocal.ThreadLocalMap threadLocals = null;
        ...
}

  

       ThreadLocal类中的主要方法

    public T get() {//threadLocal对象获取其内部Map中存放的value
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);//map的key:线程自身this
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;//该value即在不同方法中被使用到的变量/对象
        }
        return setInitialValue();
    }

 

获取当前Thread中的ThreadMap集合

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

 

设置变量到ThreadMap中

如果map为空,则初始化ThreadMap再进行保存

最后将变量返回

    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权限,在创建ThreadLocal对象时,可以复写此方法,达到初始化的目的

所谓初始化,即:将变量传入到ThreadLocal中,存放到ThreadLocalMap的value中

    protected T initialValue() {
        return null;
    }

 比如,复写初始化方法

 

创建ThreadLocalMap,将Map对象的引用传递给Thread的成员变量threadLocals

这样,即实现了ThreadLocal保存对象。

 

    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

 

获取ThreadLocal中存放的变量: 

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);//获取Thread中TheadLocalMap
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);//以ThreadLocal自己为key获取value
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }

 拿到Thread中的ThreadLocalMap

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

 

 

 ==================================================================================

一个小例子,不是很好

 

import java.text.SimpleDateFormat;
import java.util.Date;


public class DateUtil {
	private static final String DATA_FORMAT = "yyyy-MM-dd HH:mm:ss";
	
	private static ThreadLocal threadLocal = new ThreadLocal() {
		//第三步:调用重写了的initialValue(),获取到将该方法的返回值
		//以currentThread为key获取ThreadLocalMap
		//如果为空,则创建Map。key为ThreadLocal对象,value为初始化方法的返回值
		//并将Map的引用赋给Thread中的ThreadLocalMap【以后直接通过currentThread就能取出Map来,然后从Map中就拿到变量了】
		
		@Override
		protected  SimpleDateFormat initialValue() {
			return new SimpleDateFormat(DATA_FORMAT);
		}
	};
	
	public static SimpleDateFormat getDateFormat() {
		//第二步:threadLocal对象调用get()
		//get()中调用getMap(t),t为currentThread,即获取Thread类中的属性threadLocals 
		//发现ThreadLocal.ThreadLocalMap threadLocals = null;
		//开始初始化ThreadLocalMap
		//首先,将调用initialValue()初始化方法,如果子类没有复写,则返回默认值null
		return threadLocal.get();
	}
	
	public static String format(Date date) {
		return getDateFormat().format(date);
	}
	
	public static void main(String[] args) {
		final Date date = new Date();
		for(short i=0; i<10000; i++) 
			//第一步:创建Thread的实例对象t
			new Thread(new Runnable() {
				@Override
				public void run() {
					System.out.println(DateUtil.getDateFormat().format(date));
				}
			}).start();
	}
}

 

你可能感兴趣的:(WEB后台@JavaSE,java)