ThreadLocal 速读

ThreadLocal 速读

简介

ThreadLocal 可以提供线程内的局部变量,不同的线程之间不会相互干扰,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或组件之间一些公共变量传递的复杂度。

官方介绍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 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).
*
* ...
*
* <p>Each thread holds an implicit reference to its copy of a thread-local
* variable as long as the thread is alive and the {@code ThreadLocal}
* instance is accessible; after a thread goes away, all of its copies of
* thread-local instances are subject to garbage collection (unless other
* references to these copies exist).
*
* @author Josh Bloch and Doug Lea
* @since 1.2
*/
public class ThreadLocal<T>

从 Java 官方文档中的描述可以看出, ThreadLocal 类用来提供线程内部的局部变量。这种变量在多线程环境下访问(通过 get 和 set 方法访问)时能保证各个线程的变量相对独立于其他线程内的变量。ThreadLocal实例通常来说都是private static类型的,用于关联线程和线程上下文。

总结:

  1. 线程并发: 在多线程并发的场景下
  2. 传递数据: 我们可以通过 ThreadLocal 在同一线程,不同组件中传递公共变量
  3. 线程隔离: 每个线程的变量都是独立的,不会互相影响

基本使用

常用方法

在使用之前,我们先来认识几个 ThreadLocal 的常用方法

方法声明 描述
ThreadLocal() 创建ThreadLocal对象
public void set( T value) 设置当前线程绑定的局部变量
public T get() 获取当前线程绑定的局部变量
public void remove() 移除当前线程绑定的局部变量

使用案例

我们先通过一个例子来说明线程不隔离是什么样的情况:

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
public class NotSafe {

private String content;

public static void main(String[] args) {
NotSafe notSafe = new NotSafe();

for (int i = 0; i < 5; i++) {
Thread thread = new Thread(() -> {
notSafe.content = Thread.currentThread().getName() + "的数据";
System.out.println(Thread.currentThread().getName() + " ---> " + notSafe.content);
});

thread.setName("线程" + i);
thread.start();
}
}
}

// 运行结果
线程3 ---> 线程3的数据
线程2 ---> 线程1的数据
线程0 ---> 线程1的数据
线程4 ---> 线程4的数据
线程1 ---> 线程1的数据

从结果可以看出多个线程在访问同一个变量的时候出现的异常,线程间的数据没有隔离。下面我们来看下采用 ThreadLocal 的方式来解决这个问题的例子。

下面我们用 ThreadLocal 来对上面的代码进行改造

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
public class Safe {

private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();

private String getContent() {
return threadLocal.get();
}

private void setContent(String content) {
threadLocal.set(content);
}

public static void main(String[] args) {
Safe safe = new Safe();

for (int i = 0; i < 5; i++) {
Thread thread = new Thread(() -> {
safe.setContent(Thread.currentThread().getName() + "的数据");
System.out.println(Thread.currentThread().getName() + " ---> " + safe.getContent());
});

thread.setName("线程" + i);
thread.start();
}
}
}

// 运行结果
线程0 ---> 线程0的数据
线程4 ---> 线程4的数据
线程3 ---> 线程3的数据
线程1 ---> 线程1的数据
线程2 ---> 线程2的数据

从结果来看,这样很好的解决了多线程之间数据隔离的问题,十分方便。