0%

ThreadLocal线程本地存储

ThreadLocal是什么

该类提供了线程局部变量,为每一个线程创建一个单独的变量副本,使得每个线程都可以独立的改变自己所拥有的变量副本,而不会影响其他线程所对应的副本,消除了竞争条件。

采用的以空间换时间的做法,在每个Thread里维护一个以开地址法实现的ThreadLocal.ThreadLocalMap,把数据隔离
线程本地存储根除了对变量的共享。

Thread类

1
2
3
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

提供了四个方法:

  • get() 返回此线程局部变量的当前线程副本中的值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public T get() {
    // 获取当前线程
    Thread t = Thread.currentThread();
    // 获取当前线程实例的threadLocals
    ThreadLocalMap map = getMap(t);
    // 不为空
    if (map != null) {
    // 根据当前的ThreadLocal对象来取值
    ThreadLocalMap.Entry e = map.getEntry(this);
    if (e != null) {
    @SuppressWarnings("unchecked")
    T result = (T)e.value;
    return result;
    }
    }
    // 为空则设置key为当前的ThreadLocal对象,value为initialValue设置的初始值
    return setInitialValue();
    }
  • initialValue() 返回此线程局部变量当前线程的初始值,当线程第一次调用get()或set()方法时调用,并且只调用一次

  • remove() 移除此线程局部变量当前线程的值

  • set(T value) 将此线程局部变量的当前线程副本中的值设置为指定值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public void set(T value) {
    // 获取当前线程
    Thread t = Thread.currentThread();
    // 获取该线程实例对象的threadLocals
    ThreadLocalMap map = getMap(t);
    // 不为空,key为当前的ThreadLocal对象
    if (map != null)
    map.set(this, value);
    else
    // 为空,则为threadLocals实例化对象
    createMap(t, value);
    }
  • 还有一个静态内部类 ThreadLocalMap 提供了一种用键值对方式存储每一个线程的变量副本的方法,key为当前的ThreadLocal对象,value为对象线程的变量副本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class MyThread implements Runnable {

@Override
public void run() {
for (int i = 0; i < 3; i++) {
ThreadLocalVariableHolder.increment();
System.out.println(Thread.currentThread().getName() + ":" + ThreadLocalVariableHolder.get());
Thread.yield();
}
}
}

public class ThreadLocalVariableHolder {
private static ThreadLocal<Integer> myThreadLocal = new ThreadLocal<Integer>() {
// 初始值默认为null 设置初始值为0
protected Integer initialValue() {
return 0;
}
};

public static void increment() {
myThreadLocal.set(myThreadLocal.get() + 1);
}

public static int get(){
return myThreadLocal.get();
}

public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for(int i=0;i<5;i++){
executorService.execute(new MyThread());
}
executorService.shutdown();
}
}

执行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pool-1-thread-1:1
pool-1-thread-2:1
pool-1-thread-3:1
pool-1-thread-3:2
pool-1-thread-2:2
pool-1-thread-1:2
pool-1-thread-2:3
pool-1-thread-3:3
pool-1-thread-4:1
pool-1-thread-1:3
pool-1-thread-4:2
pool-1-thread-4:3
pool-1-thread-5:1
pool-1-thread-5:2
pool-1-thread-5:3

存在内存泄露问题,每次使用完ThreadLocal,都调用它的remove()方法,清除数据