关于多线程并发安全控制,我们第一反应都是用锁,但是无论是哪种锁,多多少少都对性能都有一定得影响。那么有什么方法既能保证共享资源的线程安全,又能避免锁呢?答案就是ThreadLocal。ThreadLocal可以解释成线程的局部变量,即每个线程都有变量的副本,一个线程对变量的访问都是对线程自己变量的访问,对其他线程并无影响,也就保证了对于共享资源的线程安全,又避免用锁。

ThreadLocal的基本使用

创建一个ThreadLocal对象:

  1. public class ThreadLocalTest {
  2. private ThreadLocal<Integer> localInt = new ThreadLocal<>();
  3. public void testLocal(){
  4. for (int i = 0; i < 10; i++) {
  5. final int value = i * 2;
  6. new Thread(new Runnable() {
  7. @Override
  8. public void run() {
  9. localInt.set(value);
  10. System.out.println(Thread.currentThread().getName() + ":" +localInt.get());
  11. localInt.remove();
  12. }
  13. }).start();
  14. }
  15. }
  16. public static void main(String[] args) {
  17. ThreadLocalTest localTest = new ThreadLocalTest();
  18. localTest.testLocal();
  19. }
  20. }

ThreadLocal的构造方法无参数,由于ThreadLocal是一个泛型类,这里指定了localInt的类型为整数。ThreadLocal常用的方法有:

  • set(obj):向当前线程中存储数据
  • get():获取当前线程中的数据
  • remove():删除当前线程中的数据

由于ThreadLocal里设置的值,只有当前线程自己看得见,这意味着你不可能通过其他线程为它初始化值。为了弥补这一点,ThreadLocal提供了一个withInitial()方法统一初始化所有线程的ThreadLocal的值:

  1. private ThreadLocal<Integer> localInt = ThreadLocal.withInitial(() -> 6);

ThreadLocal的实现原理

ThreadLocal可以做到变量的线程隔离(线程只可见自己的变量),那么它是怎么做到的呢?我们先从ThreadLocal的set方法开始入手:

  1. public void set(T value) {
  2. //获取当前线程
  3. Thread t = Thread.currentThread();
  4. //获取ThreadLocalMap
  5. ThreadLocalMap map = getMap(t);
  6. if (map != null)
  7. map.set(this, value); //就是map的设值
  8. else
  9. createMap(t, value); //map为空先创建再设值
  10. }

从上面的代码我们可以知道ThreadLocalMap是实现的关键。看一下getMap(t) 的实现:

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

很简单,就是从当前线程中获取ThreadLocalMap。到这里我们可以大概猜测到,所谓的ThreadLocal变量就是保存在每个线程的map中的。这个map就是Thread对象中的threadLocals字段:

  1. public class Thread implements Runnable {
  2. ...
  3. /* ThreadLocal values pertaining to this thread. This map is maintained
  4. * by the ThreadLocal class. */
  5. ThreadLocal.ThreadLocalMap threadLocals = null;
  6. ...
  7. }

关于ThreadLocalMap

未完待续…