在多线程编程中,我们使用锁(synchronized,ReentrantLock等)来确保多个线程共享的变量能被安全的访问。但是还有一些变量,我们希望每一个线程都能保存一份独立的值而不受其他线程的影响。这个时候我们就需要用到ThreadLocal这个类来完成相关操作。
ThreadLocal的作用
现有两个线程A和B,它们都能访问到一个变量str_c。在不经过任何处理时,str_c的值可以被A和B两个线程访问和修改。使用了ThreadLocal后将str_c作为一个线程的局部变量,对于每一个线程,通过ThreadLocal方法得到的str_c的值是该线程的str_c的值,这个值不会被其他线程修改,其他线程只能修改他们自己的str_c的值。在并发的代码中的任何一点我们都能访问ThreadLocal而获取str_c的值,但是获取的值是该线程的值,其他线程的str_c在这个时候被“屏蔽”了,一般情况下我们看不见且访问不到。
在工作项目中举例。在Java ee的springMVC框架中,我们在封装Controller时习惯于将request、response两个对象通过ThreadLocal封装,因为这两个对象在每个线程中的内容肯定不同,通过这种技术,可以确保在高并发情况下的安全性。
ThreadLocal与Synchronized比较
网上的部分博客对ThreadLocal与Synchronized的概念混淆不清。ThreadLocal的作用并不是取代Synchronized,这两种方法从初衷上就完全不同。不仅仅是初衷,这两种技术的实现方法也不同,不存在可比性。
ThreadLocal: 将每个线程持有的变量安全的存放起来,不被其他线程访问。
synchronized: 让多个线程并发访问的变量能够安全的被访问和操作。
ThreadLocal使用方法
ThreadLocal类本身并不是线程,这个类是用来创建并存放线程局部变量的。
方法 | 含义 |
---|---|
initialValue | 返回此线程局部变量的当前线程的“初始值”。线程第一次调用get方法前会调用此方法。 |
get | 返回此线程局部变量的当前线程副本中的值。 |
set | 将此线程局部变量的当前线程副本中的值设置为指定值。 |
remove | 移除此线程局部变量当前线程的值。 |
ThreadLocal 提供了线程局部变量并且通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。 例如,以下类通过重写initialValue方法生成对每个线程唯一的局部标识符。线程 ID 是在第一次调用 UniqueThreadIdGenerator.getCurrentThreadId() 时分配的,在后续调用中不会更改。
import java.util.concurrent.atomic.AtomicInteger;public class UniqueThreadIdGenerator {private static final AtomicInteger uniqueId = new AtomicInteger(0);private static final ThreadLocal < Integer > uniqueNum = new ThreadLocal < Integer > () {@Override protected Integer initialValue() {return uniqueId.getAndIncrement();}};public static int getCurrentThreadId() {return uniqueNum.get();}}
ThreadLocal实现原理
ThreadLocal的实现方法有些复杂,愚以为这个技术可以不采用Map的方式来存放,其他的键值对存放方式也未尝不可,但是jdk采用的方法肯定有他正确的道理。ThreadLocal存放局部变量的底层实现主要涉及到三个类:ThreadLocal、ThreadLocalMap、Thread。
ThreadLocal:
在这个类中,每当调用set方法传入值时先获得当前线程Thread对象,在Thread对象的私有变量中查看ThreadLocal.ThreadLocalMap类对象是否为空,如果不为空,将值和threadLocal的this对象传入ThreadLocalMap类中。
ThreadLocalMap:
ThreadLocalMap是TheadLocal的静态内部类,这也能够解释为什么在Thread类中ThreadLocalMap可以通过ThreadLocal.ThreadLocalMap(外部类.内部类)的方式声明。此类是一个简单实现的Map映射表和java集合框架中的Map类无关系。当有值传入时将线程对象和值以键值对的形式存放。
Thread:
这个类是当前线程类,其中包含ThreadLocal.ThreadLocalMap类型的私有变量,用该变量来存放本线程的局部变量,从而达到其他线程不能访问的效果。