1. 怎样理解 ThreadLocal
- Java 中有一个特殊的概念:线程本地变量,这个概念在 Java 中的实现类就是
ThreadLocal
,是一个泛型类 - 线程本地变量
ThreadLocal
的含义是:每个线程都有同一个变量的独有拷贝
2. 写出下面程序的运行结果并分析
1 | public class ThreadLocalBasic { |
结果:
1
2
3child thread initial: null
child thread final: 200
main thread final: 100分析:
- 主线程
main
对线程本地变量local
的设置对子线程child
不起作用 - 同样的,子线程
child
对线程本地变量local
的改变也不会影响主线程main
- 二者访问的虽然是同一个变量
local
,但每个线程都有自己的独立的值,这就是线程本地变量的含义
- 主线程
3. ThreadLocal
的主要方法有哪些
ThreadLocal
是一个泛型类,接受一个类型参数T
,它只有一个空的构造方法public T get()
:获取值,如果没有,返回null
public void set(T value)
:设置值protected T initialValue()
:提供初始值,是一个受保护方法,可以通过匿名内部类的方式提供。当调用get()
方法时,如果之前没有设置过,会调用该方法获取初始值,默认实现是返回null
public void remove()
:删掉当前线程对应的值,如果删掉后,再次调用get()
,会再调用initialValue()
获取初始值
4. 线程本地变量 ThreadLocal
的使用场景
- 日期处理
- 随机数
- 上下文信息
5. Java 中的日期和时间操作类 DateFormat/SimpleDateFormat
不是线程安全的,那实现线程安全的方法有哪些
- 使用锁
- 每次都创建一个新的对象
- 更好的方式就是使用线程本地变量
ThreadLocal
6. 写一个 ThreadLocal
在日期处理的应用实现线程安全的 Demo
1 | public class ThreadLocalDateFormat { |
7. ThreadLocal
在随机数中是怎样应用的
- 即使对象是线程安全的,使用
ThreadLocal
也可以减少竞争 Random
是线程安全的,但如果并发访问竞争激烈的话,性能会下降- 所以,Java 并发包提供了类
ThreadLocalRandom
,它是Random
的子类,利用了ThreadLocal
8. ThreadLocal
在上下文信息中的应用
ThreadLocal
的典型用途是提供上下文信息- 举个例子
- 在一个 Web 服务器中,一个线程执行用户的请求,在执行过程中,很多代码都会访问一些共同的信息,比如请求信息、用户身份信息、数据库连接、当前事务等
- 它们是线程执行过程中的全局信息,如果作为参数在不同代码间传递,代码会很烦琐。这时,使用
ThreadLocal
就很方便,所以被用于各种框架如Spring
中
9. 写一个使用 ThreadLocal
保存上下文信息的 Demo
1 | public class RequestContext { |
10. ThreadLocal
的实现原理
分析源码
- 每个线程都有一个
Map
(set()
方法内部会创建一个map
),类型为ThreadLocalMap
ThreadLocalMap
是一个内部类,是专门用于ThreadLocal
的。与一般的Map
不同,它的键类型为WeakReference<ThreadLocal>
,即弱引用,便于回收内存- 对于每个
ThradLocal
对象,调用其get()/set()
实际上就是以ThreadLocal
对象为键读写当前线程的Map
。这样,就实现了每个线程都有自己的独立副本的效果
- 每个线程都有一个
参考文章
11. 总结一下 Java 并发包中的一些同步协作工具
- 在读多写少的场景中使用
ReentrantReadWriteLock
代替 ReentrantLock,以提高性能 - 使用
Semaphore
限制对资源的并发访问数 - 使用
CountDownLatch
实现不同角色线程间的同步 - 使用
CyclicBarrier
实现同一角色线程间的协调一致
12. 简单总结下 ThreadLocal
ThreadLocal
使得每个线程对同一个变量有自己的独立副本,是实现线程安全、减少竞争的一种方案ThreadLocal
经常用于存储上下文信息,避免在不同代码间来回传递,简化代码- 每个线程都有一个
Map
,调用ThreadLocal
对象的get()/set()
方法实际上就是以ThreadLocal
对象为键读写当前线程的该Map