1. ThreadLocal
提供了本地线程的实例,即每个使用该变量的线程都会初始化一个完全独立的实例副本。 ThreadLocal变量通常private static
修饰。当一个线程结束时, 它所使用的所有ThreadLocal所生成的实例副本也会被回收。 这样做,就会让这些实例副本在线程之间完全独立,而ThreadLocal类实例在线程之间共享,通过具体的方法共享访问。
1.1. 实现
1.1.1. ThreadLocal的set方法,key是当前的线程
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
1.1.2. 原理
内部维护一个static class ThreadLocalMap
这样一个静态内部类,而这个ThreadLocalMap类中,还有一个静态内部类
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
private Entry[] table;
1.1.3. set
ThreadLocalMap中的set()方法,在出现key为null的情况下,会调用replaceStaleEntry()方法,将这个对象替换掉
private void set(ThreadLocal<?> key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
注意到Entry是继承自WeakReference这个弱引用的,当system.GC()时,无法此时JVM中内存是否足够,都要被回收。
存在的问题
- 线程安全问题:新增线程或销毁线程都要读写ThreadLocal的中map,如何确保线程安全; 可以用锁,但把map维护交由thread处理,而不是ThreadLocal。 即每个Thread只访问自己的map,就不存在写的问题。
- 内存问题:当一个线程结束,它在ThreadLocal里面的键是弱引用,但值还存在强引用的话, GC无法回收这个内存。在调用 set()、get() 、remove() 方法的时候, 会清理掉 key 为 null 的记录,即调用 replaceStaleEntry()方法。
1.2. 应用场景
- session会话
2. InheritableThreadLocal
当一个主线程中使用了线程池或衍生了多个子线程,使用ThreadLocal是不能get到值的。
/*
* Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package java.lang;
import java.lang.ref.*;
/**
* This class extends <tt>ThreadLocal</tt> to provide inheritance of values
* from parent thread to child thread: when a child thread is created, the
* child receives initial values for all inheritable thread-local variables
* for which the parent has values. Normally the child's values will be
* identical to the parent's; however, the child's value can be made an
* arbitrary function of the parent's by overriding the <tt>childValue</tt>
* method in this class.
*
* <p>Inheritable thread-local variables are used in preference to
* ordinary thread-local variables when the per-thread-attribute being
* maintained in the variable (e.g., User ID, Transaction ID) must be
* automatically transmitted to any child threads that are created.
*
* @author Josh Bloch and Doug Lea
* @see ThreadLocal
* @since 1.2
*/
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
/**
* Computes the child's initial value for this inheritable thread-local
* variable as a function of the parent's value at the time the child
* thread is created. This method is called from within the parent
* thread before the child is started.
* <p>
* This method merely returns its input argument, and should be overridden
* if a different behavior is desired.
*
* @param parentValue the parent thread's value
* @return the child thread's initial value
*/
protected T childValue(T parentValue) {
return parentValue;
}
/**
* Get the map associated with a ThreadLocal.
*
* @param t the current thread
*/
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
/**
* Create the map associated with a ThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the table.
*/
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
整个InheritableThreadLocal就是这三个方法,其他都是使用从父类继承的方法,因此,真正实现InheritableThreadLocal的过程是在Thread类中的Ini() 方法中。每个Thread类实例都有如下 两个成员变量:
// 每个线程都拥有这两个成员变量
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
// 线程在初始化过程中会判断当前主线程的inheritableThreadLocals对像不为空时,
// 构造出一个ThreadLocalMap对象
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc) {
Thread parent = currentThread();
if (parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
}
// 将当前主线程的ThreadLocalMap的值复制一份给inheritableThreadLocals
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];
for (int j = 0; j < len; j++) {
Entry e = parentTable[j];
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}
2.1. 示例
子线程无法直接获取父线程中的值,需要会用InheritableThreadLocal类
public class ThreadLocalDemo {
public static void main(String[] args) {
//ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
ThreadLocal<String> threadLocal = new ThreadLocal();
ThreadLocalDemo threadLocalDemo= new ThreadLocalDemo();
for(int i=0;i<10;++i){
String id = String.valueOf(i);
new Thread(()->{
threadLocal.set(id);
String s = threadLocal.get();
if((Integer.valueOf(id)&1)==0){
new Thread(()->{
System.out.println(Thread.currentThread().getName() + ":::" + threadLocal.get());
}).start();
}else {
System.out.println(Thread.currentThread().getName() + ":::" + s);
}
threadLocal.remove();
}).start();
}
}
}