农场主的黑科技.

并发操作合集-11.ThreadLocal

字数统计: 918阅读时长: 5 min
2018/11/04 Share

🍤 并发操作合集系列 目录

🍕 并发操作合集系列 源代码

ThreadLocal的用法

ThreadLocal用于保存某个线程共享变量:对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。

  • ThreadLocal.get: 获取ThreadLocal中当前线程共享变量的值。

  • ThreadLocal.set: 设置ThreadLocal中当前线程共享变量的值。

  • ThreadLocal.remove: 移除ThreadLocal中当前线程共享变量的值。

  • ThreadLocal.initialValue: ThreadLocal没有被当前线程赋值时或当前线程刚调用remove方法后调用get方法,返回此方法值。

示例代码:

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
private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();

public static void main(String[] args) throws InterruptedException {
Runnable task1 = () -> {
threadLocal.set(Instant.now().toString());
try {
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + " ==> " + threadLocal.get());
} catch (InterruptedException e) {
e.printStackTrace();
}
};
ExecutorService executorService = Executors.newFixedThreadPool(10);

for (int i = 0; i < 5; i++) {
executorService.submit(task1);
TimeUnit.SECONDS.sleep(1);
}
}
-----------------------------------
输出
pool-1-thread-1 ==> 2018-11-04T11:17:14.388Z
pool-1-thread-2 ==> 2018-11-04T11:17:15.387Z
pool-1-thread-3 ==> 2018-11-04T11:17:16.388Z
pool-1-thread-4 ==> 2018-11-04T11:17:17.388Z
pool-1-thread-5 ==> 2018-11-04T11:17:18.388Z

可见每个线程从ThreadLocal中取出的值,都是自身放入的值。

ThreadLocal的实现原理

为了加深理解,简单看看ThreadLocal的实现原理。

set()的源码是这样子的:

1
2
3
4
5
6
7
8
9
10
11
12
public void set(T value) {
//1. 获取当前线程实例对象
Thread t = Thread.currentThread();
//2. 通过当前线程实例获取到ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
if (map != null)
//3. 如果Map不为null,则以当前threadLocl实例为key,值为value进行存入
map.set(this, value);
else
//4.map为null,则新建ThreadLocalMap并存入value
createMap(t, value);
}

通过源码我们知道value是存放在了ThreadLocalMap里了。这个ThreadLocalMap是从哪里取出来的,我们看一下源码中的getMap(t)方法:

1
2
3
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}

也就是说这个ThreadLocalMap是我们当前Thread中的一个叫threadLocals的成员变量,ThreadLocalMap是由当前线程自身持有的。

当这个map为null时,说明当前线程还没创建过ThreadLocalMap,所以通过createMap(t,value)创建ThreadLocalMap并放入线程中。

对set方法进行总结一下: 通过当前线程对象thread获取该thread所维护的threadLocalMap,若threadLocalMap不为null,则以threadLocal实例为key,值为value的键值对存入threadLocalMap,若threadLocalMap为null的话,就新建threadLocalMap然后在以threadLocal为键,值为value的键值对存入即可。

get()方法和remove()实际上也都是调用了这个当前Thread中维护的threadLocalMap。这里就不多做解释了。

ThreadLocal的使用场景

ThreadLocal 不是用来解决共享对象的多线程访问问题的,数据实质上是放在每个thread实例引用的threadLocalMap,也就是说每个不同的线程都拥有专属于自己的数据容器(threadLocalMap),彼此不影响。因此threadLocal只适用于 共享对象会造成线程安全 的业务场景。比如hibernate中通过threadLocal管理Session就是一个典型的案例,不同的请求线程(用户)拥有自己的session,若将session共享出去被多线程访问,必然会带来线程安全问题。

Reference

ThreadLocal用法详解和原理

并发容器之ThreadLocal

CATALOG
  1. 1. ThreadLocal的用法
  2. 2. ThreadLocal的实现原理
  3. 3. ThreadLocal的使用场景
  4. 4. Reference