这样就可以调用 setValue
设置线程变量,调用 getValue
方法获取线程变量。
通过 Thread 类操作线程变量,每次需要先调用 Thread.currentThread()
获取当前线程对象,这部分代码可以封装起来。新增一个 ThreadLocal
类,封装了线程变量的操作功能。
1 | public class ThreadLocal { |
这样在使用时只需要调用 ThreadLocal.get()
和 ThreadLocal.set()
来操作线程变量,更加方便。
1 | // 设置变量值 |
上面实现了线程变量的功能,但是每个线程只能保存一个变量,这在使用时有很大的局限性,如果有超过一个变量需要保存就无能为力了。
为支持多个变量,可使用 ThreadLocal
指定要保存的变量和类型,通过 ThreadLocal
变量进行操作。
1 | public class ThreadLocal<T> { |
这里将 ThreadLocal
的 get
和 set
方法改为了通过对象调用,根据 ThreadLocal
对象的不同确定要操作的变量。
1 | // 定义两个 ThreadLocal |
要保存多个变量,首先要对数据结构进行调整。再看下获取变量的代码
1 | private T getFromThread(Thread thread) { |
这里是从 Thread
对象中获取线程变量,具体来讲是:根据调用者 ThreadLocal
去 Thread
对象中获取对应的变量。伪代码如下:
1 | // 根据调用者 ThreadLocal 去 Thread 对象中获取对应的变量 |
很显然可以使用 map 保存数据,map 的 key 为 ThreadLocal
对象, map 的 value 为要保存的变量。因此定义结构如下:
将原来的 Object value
改为 Map<ThreadLocal, Object> threadLocalMap
,用于保存多个变量。
这样 getFromThread
的功能也可以实现了,setToThread
方法类似
1 | private T getFromThread(Thread thread) { |
通常情况下把 ThreadLocal 变量定义为类的静态属性,通过静态方法暴露外部接口,实现工具类功能。
1 | private static final ThreadLocal<Integer> INTEGER_THREADLOCAL = new ThreadLocal<Integer>(); |
ThreadLocal 的实现原理基本上如上文所讲,但是实际代码比这要复杂很多,需要注意的是用于保存变量的 map 是一个定制化的 HashMap:ThreadLocalMap,它的 key 使用了 WeakReference 以支持垃圾回收。
引用关系图如下:
程晓明,方腾飞,魏鹏. Java并发编程的艺术
多线程带来了性能的提升,但是在读写共享变量时也带来了线程安全问题。你可以对共享变量加锁,包括 synchronized 内置锁、ReentrantLock、读写锁等等,这样可以实现多个线程的读写安全。如果每个线程可以独立访问自己的数据,那么就不存在线程安全的问题,前提是可以为每个线程创建副本,副本之间保持独立,这也就是 ThreadLocal 的实现思路。
Java 线程共有 6 种状态,在任意时刻只能处于其中一种状态,随着代码的执行状态也会发生变化。