不同的线程中,同一个ThreadLocal中的值的对象不一样,且其它 Thread 不可访问。
ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程间隔离而在方法或类间共享的场景
比如,ThreadLocal
1.创建并初始化(支持泛型)(或者可以使用重写initialValue方法来初始化)
使用
private static ThreadLocal
counter = ThreadLocal.withInitial(() -> new StringBuilder());
2.set方法
使用
counter.set(str.append(j));
3.get方法
使用
StringBuilder str = counter.get();
1.在主线程中定义ThreadLocal并初始化
2.在主线程中启动3个线程thread-1、thread-2、thread-3,主线程等待这三个线程执行完后统计一共的时间。
3.在线程thread-1、thread-2、thread-3中修改ThreadLocal的值。
package com.sid.threadlocal;
import java.util.concurrent.CountDownLatch;
/**
* @program: thread-test
* @description:
* @author: Sid
* @date: 2018-11-26 10:38
* @since: 1.0
**/
public class ThreadLocalTest {
private static ThreadLocal counter = ThreadLocal.withInitial(() -> new StringBuilder());
public static void main(String[] args) throws InterruptedException {
int threads = 3;
CountDownLatch countDownLatch = new CountDownLatch(threads);
System.out.printf("main thread start:"+System.currentTimeMillis());
for(int i = 1; i <= threads; i++) {
new Thread(() -> {
for(int j = 0; j < 4; j++) {
StringBuilder str = counter.get();
counter.set(str.append(j));
System.out.printf("Thread name:%s , ThreadLocal hashcode:%s, Instance hashcode:%s, Value:%s\n",
Thread.currentThread().getName(),
counter.hashCode(),
counter.get().hashCode(),
counter.get().toString());
}
counter.set(new StringBuilder("hello world"));
System.out.printf("Set, Thread name:%s , ThreadLocal hashcode:%s, Instance hashcode:%s, Value:%s\n",
Thread.currentThread().getName(),
counter.hashCode(),
counter.get().hashCode(),
counter.get().toString());
countDownLatch.countDown();
}, "thread - " + i).start();
}
countDownLatch.await();
System.out.printf("main thread end:"+System.currentTimeMillis());
}
}
main thread start:1543202951571Thread name:thread - 1 , ThreadLocal hashcode:247001773, Instance hashcode:8326410, Value:0
Thread name:thread - 3 , ThreadLocal hashcode:247001773, Instance hashcode:989188200, Value:0
Thread name:thread - 1 , ThreadLocal hashcode:247001773, Instance hashcode:8326410, Value:01
Thread name:thread - 1 , ThreadLocal hashcode:247001773, Instance hashcode:8326410, Value:012
Thread name:thread - 2 , ThreadLocal hashcode:247001773, Instance hashcode:469347494, Value:0
Thread name:thread - 1 , ThreadLocal hashcode:247001773, Instance hashcode:8326410, Value:0123
Thread name:thread - 3 , ThreadLocal hashcode:247001773, Instance hashcode:989188200, Value:01
Thread name:thread - 3 , ThreadLocal hashcode:247001773, Instance hashcode:989188200, Value:012
Thread name:thread - 3 , ThreadLocal hashcode:247001773, Instance hashcode:989188200, Value:0123
Set, Thread name:thread - 3 , ThreadLocal hashcode:247001773, Instance hashcode:875702598, Value:hello world
Set, Thread name:thread - 1 , ThreadLocal hashcode:247001773, Instance hashcode:1892279149, Value:hello world
Thread name:thread - 2 , ThreadLocal hashcode:247001773, Instance hashcode:469347494, Value:01
Thread name:thread - 2 , ThreadLocal hashcode:247001773, Instance hashcode:469347494, Value:012
Thread name:thread - 2 , ThreadLocal hashcode:247001773, Instance hashcode:469347494, Value:0123
Set, Thread name:thread - 2 , ThreadLocal hashcode:247001773, Instance hashcode:356405673, Value:hello world
main thread end:1543202951600
set源码
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源码
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();
}
createMap(t,value)是创建了ThreadLocalMap
ThreadLocalMap threadLocals = new ThreadLocalMap(t, value);
getMap(t)方法是返回了ThreadLocalMap。
解读:
可以看到ThreadLocal 维护了一个 ThreadLocalMap,key是 Thread,value是它在该 Thread 内的实例。
线程通过该 ThreadLocal 的 get() 获取实例时,只需要以线程为key,从 Map 中找出对应的实例。
我们来看一下ThreadLocalMap源码主要部分
ThreadLocalMap.Entry的实现
static class Entry extends WeakReference> {
Object value;
Entry(ThreadLocal> k, Object v) {
super(k);
value = v;
}
}
Entry的key是个弱引用,Entry的key即ThreadLocal不会发生内存泄漏。
既然Entry的key即ThreadLocal是个弱引用,那么内存泄漏可能会发生在什么地方呢?
内存泄漏可能会发生在Entry或Entry.value。Entry的key即ThreadLocal是个弱引用,垃圾回收器回收的是Entry的key即是ThreadLocal,而不是Entry,更不是Entry.value。
ThreadLocalMap使用ThreadLocal的弱引用作为key,
如果一个ThreadLocal没有外部强引用来引用它,
那么系统 GC 的时候,这个ThreadLocal就会被回收,
但是Entry.key被回收后,ThreadLocalMap中就会出现key为null的Entry,
就没有办法访问这些key为null的Entry.value,
如果当前线程一直不结束,
这些key为null的Entry.value就会一直存在一条强引用链:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value
此时的Entry.value永远无法回收,这样就有可能发生内存泄漏。
虽然ThreadLocalMap的设计中已经考虑到这种情况,也加上了一些方法设置Entry=null和Entry.value=null:
在ThreadLocalMap的getEntry(),set(),remove()的时候都会调用
1.replaceStaleEntry(),将所有Entry.key=null 的 Entry.value设置为null。
2.rehash(),通过 expungeStaleEntry()方法将Entry.key和Entry.value都=null的Entry设置为 null。
1.使用static的ThreadLocal,延长了ThreadLocal的生命周期。
2.分配使用了ThreadLocal又不再调用ThreadLocalMap的getEntry(),set(),remove()方法。
每次使用完ThreadLocal,都调用它的remove()方法,清除数据。