java中的ThreadLocal,顾名思义就是线程本地信息。官网给出的定义以下:java
This class provides thread-local variables.These variables differ from
their normal counterparts in that each thread that accesses one (via its
{@code get} or {@code set} method) has its own, independently initialized
copy of the variable. {@code ThreadLocal} instances are typically private
static fields in classes that wish to associate state with a thread (e.g.,
a user ID or Transaction ID).
复制代码
ThreadLocal的方法安全
public T get()
public void set(T value)
public void remove()
复制代码
以set方法为例,看下具体实现bash
/**
* 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);
}
复制代码
这里又出现一个ThreadLocalMap类,经过getMap方法获得,参数为当前线程tide
/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
复制代码
这个getMap方法获取的就是当前线程的局部变量threadLocals,其定义以下:ui
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
复制代码
因此ThreadLocal类的get/set方法,其实都是首先获取当前线程t,而后根据线程t获得线程的ThreadLocalMap变量,接下来就是对这个线程变量进行操做,这里才明白什么叫作线程局部变量。this
/**
* ThreadLocalMap is a customized hash map suitable only for
* maintaining thread local values. No operations are exported
* outside of the ThreadLocal class. The class is package private to
* allow declaration of fields in class Thread. To help deal with
* very large and long-lived usages, the hash table entries use
* WeakReferences for keys. However, since reference queues are not
* used, stale entries are guaranteed to be removed only when
* the table starts running out of space.
*/
复制代码
其实ThreadLocalMap就是一个自定义实现的HashMap,他的key就是ThreadLocal类型的变量 ThreadLocal的set方法会调用ThreadLocalMap的方法,看下方法的定义spa
private void set(ThreadLocal<?> key, Object value)
复制代码
这里能够看出一个Thread能够存储不一样类型的数据。线程
这里注意一点引发迷惑的就是明明是每一个线程本地的信息,为何又要定义成 static field,这不是有点矛盾么?并且为何又是 typically,那非typically呢?code
static变量是类静态变量,即一个线程内,static变量是被各个实例共同引用的, 若是把ThreadLocal声明为非static变量,那么每建立一个该类的实例就会建立一个新的实例对象,那么同一个线程就会访问到同一个实例对象的不一样ThreadLocal对象,而分析ThreadLocal类可知,事实上ThreadLocal自己是不存储数据的,只是对Thread类对象的局部变量进行操做,因此定义为static更合适,不然虽然不会致使错误,也会致使资源浪费。orm
举例以下, 若是设置为static型变量
public class ThreadLocalUtils {
private static ThreadLocal<String> stringThreadLocal = new ThreadLocal<>();
public static String getThreadLocal() {
return stringThreadLocal.get();
}
public static void setThreadLocal(String value) {
stringThreadLocal.set(value);
}
public static void main(String[] args) {
List<Integer> list = new LinkedList<>();
for (int i=0; i<4; i++) {
list.add(i);
}
list.parallelStream().forEach(integer -> {
ThreadLocalUtils.setThreadLocal(String.valueOf(integer));
System.out.println("Start-"+Thread.currentThread().getName()+":"+ThreadLocalUtils.getThreadLocal());
try {
Thread.sleep(900);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("End-"+Thread.currentThread().getName()+":" + ThreadLocalUtils.getThreadLocal());
});
}
}
复制代码
输出结果为:
Start-ForkJoinPool.commonPool-worker-2:3
Start-ForkJoinPool.commonPool-worker-1:1
Start-ForkJoinPool.commonPool-worker-3:0
Start-main:2
End-main:2
End-ForkJoinPool.commonPool-worker-3:0
End-ForkJoinPool.commonPool-worker-2:3
End-ForkJoinPool.commonPool-worker-1:1
复制代码
若是设置为非static类型变量:
public class ThreadLocalUtils {
private ThreadLocal<String> stringThreadLocal = new ThreadLocal<>();
public String getThreadLocal() {
return stringThreadLocal.get();
}
public void setThreadLocal(String value) {
stringThreadLocal.set(value);
}
public static void main(String[] args) {
List<Integer> list = new LinkedList<>();
for (int i=0; i<4; i++) {
list.add(i);
}
list.parallelStream().forEach(integer -> {
ThreadLocalUtils utils = new ThreadLocalUtils();
utils.setThreadLocal(String.valueOf(integer));
System.out.println("Start-"+Thread.currentThread().getName()+":"+utils.getThreadLocal());
try {
Thread.sleep(900);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("End-"+Thread.currentThread().getName()+":" + utils.getThreadLocal());
});
}
}
复制代码
输出以下:
Start-main:2
Start-ForkJoinPool.commonPool-worker-2:3
Start-ForkJoinPool.commonPool-worker-3:0
Start-ForkJoinPool.commonPool-worker-1:1
End-main:2
End-ForkJoinPool.commonPool-worker-1:1
End-ForkJoinPool.commonPool-worker-2:3
End-ForkJoinPool.commonPool-worker-3:0
复制代码
二者的输出结果彻底相同,可是为非static型变量就会出现类变量的重复定义。
若是将stringThreadLocal变为String类型变量就会变成
Start-main:3
Start-ForkJoinPool.commonPool-worker-1:1
Start-ForkJoinPool.commonPool-worker-3:0
Start-ForkJoinPool.commonPool-worker-2:3
End-ForkJoinPool.commonPool-worker-3:1
End-main:1
End-ForkJoinPool.commonPool-worker-1:1
End-ForkJoinPool.commonPool-worker-2:1
复制代码
能够看到ThreadLocal变量和非ThreadLocal变量的区别了。
那么非typically的场景是什么呢,就是在单例模式下,不须要指定为static:让类自己成为一个单例对象,这样只要全局中有可用的单例对象,就能够安全地使用实例级ThreadLocal。