一、用法
ThreadLocal用于保存某个线程共享变量:对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。
- ThreadLocal.get::获取ThreadLocal中当前线程共享变量的值。
- ThreadLocal.set:设置ThreadLocal中当前线程共享变量的值。
- ThreadLocal.remove: 移除ThreadLocal中当前线程共享变量的值。
- ThreadLocal.initialValue: ThreadLocal没有被当前线程赋值时或当前线程刚调用remove方法后调用get方法,返回此方法值。
package test;
/**
* @author
*/
public class MyThreadLocal {
private static final ThreadLocal<Object> threadLocal = new ThreadLocal<Object>() {
/**
* ThreadLocal没有被当前线程赋值时或当前线程刚调用remove方法后调用get方法,返回此方法值
*/
@Override
protected Object initialValue() {
System.out.println("调用ThreadLocal的get方法,当线程共享变量没有设置,调用initialValue获取默认值");
return super.initialValue();
}
};
public static void main(String[] args) {
new Thread(new MyIntegerTask("线程1", 10)).start();
new Thread(new MyIntegerTask("线程2", 20)).start();
}
public static class MyIntegerTask implements Runnable {
private String name;
private Integer index;
public MyIntegerTask(String name, Integer index) {
this.name = name;
this.index = index;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
//ThreadLocal.get方法获取线程变量
if (null == MyThreadLocal.threadLocal.get()) {
//获取线程变量为空
MyThreadLocal.threadLocal.set(index);
System.out.println(name + ":设置共享变量值为:" + index);
} else {
int num = (Integer)MyThreadLocal.threadLocal.get();
MyThreadLocal.threadLocal.set(num + 1);
System.out.println(name + ":共享变量值为:" + MyThreadLocal.threadLocal.get());
if (i == 3) {
MyThreadLocal.threadLocal.remove();
System.out.println(name + ":执行remove操作");
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
输出结果:
关键点:i=3的时候,进行了remove操作
二:原理
Thread.ThreadLocalMap<ThreadLocal, Object>;线程共享变量缓存如下:
1、Thread: 当前线程,可以通过Thread.currentThread()获取。
2、ThreadLocal:我们的static ThreadLocal变量。
3、Object: 当前线程共享变量。
我们调用ThreadLocal.get方法时,实际上是从当前线程中获取ThreadLocalMap<ThreadLocal, Object>,然后根据当前ThreadLocal获取当前线程共享变量Object。
/**
* 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) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//get无结果,则执行initialValue方法
return setInitialValue();
}
ThreadLocal.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);
}
ThreadLocal.remove
/**
* Removes the current thread's value for this thread-local
* variable. If this thread-local variable is subsequently
* {@linkplain #get read} by the current thread, its value will be
* reinitialized by invoking its {@link #initialValue} method,
* unless its value is {@linkplain #set set} by the current thread
* in the interim. This may result in multiple invocations of the
* {@code initialValue} method in the current thread.
*
* @since 1.5
*/
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
crateMap:
/**
* Create the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the map
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
/**
* Construct a new map initially containing (firstKey, firstValue).
* ThreadLocalMaps are constructed lazily, so we only create
* one when we have at least one entry to put in it.
*/
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
三:存储结构优点
- 线程死去的时候,线程共享变量ThreadLocalMap则销毁。
- ThreadLocalMap<ThreadLocal,Object>键值对数量为ThreadLocal的数量,一般来说ThreadLocal数量很少,相比在ThreadLocal中用Map<Thread, Object>键值对存储线程共享变量(Thread数量一般来说比ThreadLocal数量多),性能提高很多。
四:弱引用问题
当线程没有结束,但是ThreadLocal已经被回收,则可能导致线程中存在ThreadLocalMap<null, Object>的键值对,造成内存泄露。(ThreadLocal被回收,ThreadLocal关联的线程共享变量还存在)。
虽然ThreadLocal的get,set方法可以清除ThreadLocalMap中key为null的value,但是get,set方法在内存泄露后并不会必然调用,所以为了防止此类情况的出现,我们有两种手段。
1、使用完线程共享变量后,显示调用ThreadLocalMap.remove方法清除线程共享变量。
2、JDK建议ThreadLocal定义为private static,这样ThreadLocal的弱引用问题则不存在了。