Java 中 ThreadLocal 的使用解析

来源:互联网 发布:即时通讯软件 编辑:程序博客网 时间:2024/06/05 05:09

什么是 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: 4ThreadName: thread1, ThreadLocalValue: 3ThreadName: thread2, ThreadLocalValue: 6ThreadName: thread1, ThreadLocalValue: 5ThreadName: thread1, ThreadLocalValue: 7ThreadName: thread2, ThreadLocalValue: 8ThreadName: thread1, ThreadLocalValue: 9ThreadName: thread2, ThreadLocalValue: 10ThreadName: thread1, ThreadLocalValue: 11ThreadName: thread2, ThreadLocalValue: 12ThreadName: thread1, ThreadLocalValue: 13ThreadName: 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

原创粉丝点击