* 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类用来提供线程内部的局部变量。这种变量在多线程环境下访问(通过get或set方法访问)时能保证各个线程里的变量相对独立于其他线程内的变量。ThreadLocal实例通常来说都是private static
类型的,用于关联线程和线程的上下文。
ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度
ThreadLocal不是同步机制,也不解决共享对象的多线程竞态条件问题。
ThreadLocal用来辅助平衡效率与线程的资源分配。
ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的并发访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。
ThreadLocal
set(T):保存对象
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():当某个线程初次调用ThreadLocal.get方法时,就会调用initialValue()来获取初始值,否则返回由当前执行线程在调用set(T)的最新值,该方法避免了将这个对象作为参数传递的麻烦。
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;
}
}
return setInitialValue();
}
(1)获取当前线程
(2)根据当前线程获取一个map
(3)如果获取的map不为空,则在map中以ThreadLocal的引用作为key来在map中获取对应的value e,否则转到(5)
(4)如果e不为null,则返回e.value,否则转到(5)
(5)Map为空或者e为空,则通过initialValue函数获取初始值value,然后用ThreadLocal的引用和value作为firstKey和firstValue创建一个新的Map
remove():将当前线程的ThreadLocal绑定的值删除
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
initialValue():用于设置 ThreadLocal 的初始值,默认返回 null,该函数是protected类型的,通常该函数都会以匿名内部类的形式被重载,以指定初始值
protected T initialValue() {
return null;
}
getMap(Thread):获取线程的ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
createMap(Thread,T):初始化线程的threadLocals,然后设定key-value
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
createInheritedMap(ThreadLocalMap):
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
nextHashCode():
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
setInitialValue():
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;
}
JDK1.3:ThreadLocal<T>视为包含了Map<Thread,T>对象,其中保存了特定于该线程的值。然后用线程的ID作为Map的key,实例对象作为Map的value,这样就能达到各个线程的值隔离的效果
JDK1.8:每个Thread维护一个ThreadLocalMap映射表,这个映射表的key是ThreadLocal实例本身,value是真正需要存储的Object。
JDK1.8优势
- 每个Map的Entry数量变小了:之前是Thread的数量,现在是ThreadLocal的数量,能提高性能
- 当Thread销毁之后对应的ThreadLocalMap也就随之销毁了,能减少内存使用量
每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。
java.lang.Thread:
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
ThreadLocal不能继承父线程的ThreadLocal的内容, InheritableThreadLocal类继承于ThreadLocal类,InheritableThreadLocal变量值会自动传递给所有子线程,在父子线程之间传递数据。 创建一个线程时如果保存了所有
InheritableThreadLocal 对象的值,那么这些值也将自动传递给子线程。如果一个子线程调用
InheritableThreadLocal 的 get()
,那么它将与它的父线程看到同一个对象。为保护线程安全性,您应该只对不可变对象(一旦创建,
其状态就永远不会被改变的对象)使用InheritableThreadLocal ,因为对象被多个线程共享。
InheritableThreadLocal合适用于把数据从父线程传到子线程,
很例如用户标识(user id)或事务标识(transaction id),但不能是有状态对象,例如 JDBC
Connection 。
private static ThreadLocal<Integer> integerThreadLocal = new ThreadLocal<>();
private static InheritableThreadLocal<Integer> inheritableThreadLocal =
new InheritableThreadLocal<>();
@Test
public void inheritableThreadLocal() {
// 父线程
integerThreadLocal.set(1);
inheritableThreadLocal.set(1);
//结果:pool-1-thread-1:null/1
threadFactory.newThread(() -> System.out.println(Thread.currentThread().getName() + ":"
+ integerThreadLocal.get() + "/"
+ inheritableThreadLocal.get())).start();
}
Reference(引用)
- 强引用(FinalReference、Finalizer )
类似“Object obj =new Object()”这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。强引用是Java的默认引用实现, 它会尽可能长时间的存活于JVM内, 当没有任何对象指向它时, GC执行后也不会被回收。如果一个对象具有强引用,那就类似于必不可少的生活用品, 垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误, 使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。JVM 系统采用 Finalizer 来管理每个强引用对象 , 并将其被标记要清理时加入 ReferenceQueue, 并逐一调用该对象的 finalize() 方法
Object obj = new Object();
Object strongReference = obj;
Assertions.assertSame(obj, strongReference);
obj = null;
System.gc();
Assertions.assertNull(obj);
Assertions.assertNotNull(strongReference);
Finalizer是FinalReference的子类,该类被final修饰,不可再被继承,JVM实际操作的是Finalizer。当一个类满足实例化FinalReference的条件时,JVM会调用Finalizer.register()进行注册。
- 软引用(java.lang.ref.SoftReference)
Object obj = new Object();
SoftReference<Object> softReference = new SoftReference<>(obj);
Assertions.assertNotNull(softReference.get());
obj = null;
System.gc();
Assertions.assertNull(obj);
// SoftReference 只有在 jvm OutOfMemory 之前才会被回收, 所以它非常适合缓存应用
Assertions.assertNotNull(softReference.get());
- 弱引用(java.lang.ref.WeakReference)
用来描述非必须对象的,但它的强度比软引用要弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。
Object obj = new Object();
WeakReference<Object> weakReference = new WeakReference<>(obj);
Assertions.assertSame(obj, weakReference.get());
obj = null;
System.gc();
Assertions.assertNull(weakReference.get());
Map<Object, Object> weakHashMap = new WeakHashMap<>();
Object key = new Object();
Object value = new Object();
weakHashMap.put(key, value);
Assertions.assertTrue(weakHashMap.containsValue(value));
key = null;
System.gc();
Thread.sleep(1000);
// 一旦没有指向 key 的强引用, WeakHashMap 在 GC 后将自动删除相关的 entry
Assertions.assertFalse(weakHashMap.containsValue(value));
- 虚引用(java.lang.ref.PhantomReference)