Java 中 ThreadLocal 的使用解析

什么是 ThreadLocal

ThreadLocal 是 Java 中的一个基础类,其定义如下:

This class provides thread-local variables. These variables differ from 
their normal counterparts in that each thread that accesses one (via 
its get or set method) has its own, independently initialized copy of 
the variable. 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).

ThreadLocal 变量和其他变量的不同点在于,每个访问 ThreadLocal 变量的线程都拥有一个属于他自己的独立初始化的变量副本,不同线程对同一个 ThreadLocal 变量的读取和写入操作互不干扰。使用 ThreadLocal 可以将一个变量和一个线程绑定,使同一个变量在不同线程访问时能够起到数据隔离的作用。网络上有一些文章将 ThreadLocal 和 synchronized 的使用相提并论,我觉得两者的适用场景是不同的,ThreadLocal 解决的是在多线程环境下的数据隔离问题,而 synchronized 解决的是多线程对于共享变量的并发访问问题,两者并没有太多的可比性。

ThreadLocal的实现

ThreadLocal 的具体实现可以阅读JDK的源码,有几个要点说明如下:
(1) 对一个 ThreadLocal 变量的 set/get 操作,都需要通过一个 ThreadLocalMap 来实现,这个 ThreadLocalMap 是和当前线程相关联的,他在 ThreadLocal 类中定义,但并不是 ThreadLocal 的实例变量,而是 Thread 类的实例变量。可以参考 ThreadLocal#set() 方法和 getMap() 方法的代码:

/**
 * Sets the current thread's copy of this thread-local variable
 * to the specified value.  Most subclasses will have no need to
 * override this method, relying solely on the {@link #initialValue}
 * method to set the values of thread-locals.
 *
 * @param value the value to be stored in the current thread's copy of
 *        this thread-local.
 */
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}
/**
 * Get the map associated with a ThreadLocal. Overridden in
 * InheritableThreadLocal.
 *
 * @param  t the current thread
 * @return the map
 */
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

(2) 从上述第一点的 ThreadLocal#set() 方法的代码可以看出,ThreadLocalMap 中存储的 key 就是 ThreadLocal 自己,value 是用户设置的 ThreadLocal 的值。ThreadLocalMap 是采用一个 ThreadLocalMap.Entry 数组存储这些 value 的,通过key 的哈希值找到对应的数组下标,再对数组里面相应的值做读取或写入操作。
(3) ThreadLocalMap 是 Thread 的实例变量,因此每个 Thread 实例都持有一个单独的 ThreadLocalMap ,不同 Thread 中操作的 ThreadLocal 变量才能实现数据隔离。

public class Thread implements Runnable {
    ...
    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
    ...
}

ThreadLocal的使用场景

ThreadLocal 可以用于多线程应用程序中需要进行数据隔离的场景,比如你希望一个线程中对某个实例变量的访问和修改操作仅对当前线程产生影响,而对其他同时运行的线程不可见。一个简单的例子如下:

public class ThreadLocalExample {

    public static class MyRunnable implements Runnable {

        private ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
            @Override
            protected Integer initialValue()
            {
                return 0;
            }
        };

        @Override
        public void run() {
            Thread t = Thread.currentThread();
            String name = t.getName();

            if ("thread1".equals(name)) {
                threadLocal.set(1);
            } else if ("thread2".equals(name)) {
                threadLocal.set(2);
            }

            for (int i = 0; i <= 5; i++) {
                int currVal = threadLocal.get();
                threadLocal.set(currVal + 2);
                System.out.println("ThreadName: " + name + ", ThreadLocalValue: " + threadLocal.get());
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {}
            }
        }
    }


    public static void main(String[] args) throws InterruptedException
    {
        MyRunnable task = new MyRunnable();

        Thread thread1 = new Thread(task, "thread1");
        Thread thread2 = new Thread(task, "thread2");
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
    }

}

这个例子中,thread1线程产生奇数,而thread2线程产生偶数,两个线程都使用了同一个名为 threadLocal 的变量,由于该变量是 ThreadLocal 变量,因此这两个线程对threadLocal 的读写操作互不干扰。
这个例子的输出如下(下列输出打印的顺序因实际运行环境而异)

ThreadName: thread2, ThreadLocalValue: 4
ThreadName: thread1, ThreadLocalValue: 3
ThreadName: thread2, ThreadLocalValue: 6
ThreadName: thread1, ThreadLocalValue: 5
ThreadName: thread1, ThreadLocalValue: 7
ThreadName: thread2, ThreadLocalValue: 8
ThreadName: thread1, ThreadLocalValue: 9
ThreadName: thread2, ThreadLocalValue: 10
ThreadName: thread1, ThreadLocalValue: 11
ThreadName: thread2, ThreadLocalValue: 12
ThreadName: thread1, ThreadLocalValue: 13
ThreadName: thread2, ThreadLocalValue: 14

[1] A Painless Introduction to Java’s ThreadLocal Storage
[2] Java ThreadLocal
[3] When and how should I use a ThreadLocal variable?
[4] Java Concurrency in Practice

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值