Java中的ThreadLocal
来源:互联网 发布:淘宝摄影布光 编辑:程序博客网 时间:2024/06/05 00:18
这一篇之所以讲ThreadLocal,是因为之前在读Handler,Looper的源码过程(见http://maosidiaoxian.iteye.com/blog/1927735)中,看到了这个类,引起了我的兴趣。而后来发现JAVA1.6中的TheadLocal类,和我在android源码看到的这个ThreadLocal类代码是不一样的。所以这篇先讲一下Java的ThreadLocal。
Java中ThreadLocal在Java1.2就已经提出了,后来重构过,所以我在想android中的这个类的实现是不是重构过之前的版本优化过的,由于未找到早前版本该类的代码,所以只能进行猜测。
ThreadLocal用于提供线程局部变量,即通过这里的get()和set()方法访问某个变量的线程都有自己的局部变量。但在这里需要注意的是,它是通过各个线程自己创建对象然后调用这里的set方法设置进去,而不是由ThreadLocal自己来创建变量的副本的。就像上面链接的那一篇文章里面提到的android的Looper类所用的(虽然Android里ThreadLocal实现代码不一样,但用法是一样的),代码如下:
可以看到,线程中的局部变量,是在线程中创建,然后调用ThreadLocal.set()方法设置进去的。
下面详细读一下ThreadLocal这个类,看看它是怎么实现的(jdk1.6的源码)。
从上图可以看到ThreadLocal有三个变(常)量,代码如下:
其中HASH_INCREMENT 是一个常量,它表示连续的两个ThreadLocal实例的threadLocalHashCode值的增量。至于为什么用0x61c88647常量,我也没明白,只百度出一堆跟加密算法有关的内容。nextHashCode变量是AtomicInteger类型,AtomicInteger是一个提供原子操作的Integer类,简单而言就是不用Synchronized关键字也可以进行线程安全的加减操作。
当创建一个ThreadLocal对象时,会进行以下操作:
我们再来看nextHashCode()这个方法,代码如下:
它是返回一个hash code,同时对nextHashCode加上增量HASH_INCREMENT。也就是在初始化ThreadLocal的实例过程中,做的仅仅是将一个哈希值赋给实例的threadLocalHashCode,并生成下一个哈希值。而前面可以看到threadLocalHashCode是final的,在个类中,它用来区分不同的ThreadLocal对象。
在前面的图当中,我们可以看到在ThreadLocal里还有一个ThreadLocalMap的内部类,从类名来看应该是被设计来保存线程局部变量的Map,但是在ThreadLocal类或它的实例当中,并没有其他变量,那么通过ThreadLocal.set()放进去的值又是怎么保存的呢?
我们继续看ThreadLocal的set()和其他方法:
从上面的代码可以看到,set()方法是调用ThreadLocalMap.set()方法保存到ThreadLocalMap实例当中,但是ThreadLocalMap实例却不是保存在Thread中,它通过getMap()方法去取,当取不到的时候就去创建,但是创建之后却是保存在Thread实例中。可以看到Thread.java的代码有如下声明:
这种设计很巧妙,线程中有各自的map,而把ThreadLocal实例作为key,这样除了当线程销毁时相关的线程局部变量被销毁之外,还让性能提升了很多。
看完set()方法,再来看get()方法,它的代码如下:
可以看到在get()方法中,是通过当前线程获取map,再从map当中取得对象。如果取不到(如map为null或取到的值为null),则调用setInitialValue()方法对其设置初始值。setInitialValue()方法会调用initialValue()方法得到一个初始值(默认为null),然后当Thread中的map不为空时,把初始值设置进去,否则为它创建一个ThreadLocalMap对象(但不会调用map的set方法保存这个初始值),最后返回这个初始值。
最后看remove()方法,它提供了移除此线程局部变量在当前进程的值。代码如下:
关于ThreadLocal就读到这里,有时间再写下它的内部类ThreadLocalMap。
参考文章:
lujh99. 正确理解ThreadLocal. http://www.iteye.com/topic/103804
Java中ThreadLocal在Java1.2就已经提出了,后来重构过,所以我在想android中的这个类的实现是不是重构过之前的版本优化过的,由于未找到早前版本该类的代码,所以只能进行猜测。
ThreadLocal用于提供线程局部变量,即通过这里的get()和set()方法访问某个变量的线程都有自己的局部变量。但在这里需要注意的是,它是通过各个线程自己创建对象然后调用这里的set方法设置进去,而不是由ThreadLocal自己来创建变量的副本的。就像上面链接的那一篇文章里面提到的android的Looper类所用的(虽然Android里ThreadLocal实现代码不一样,但用法是一样的),代码如下:
- static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
- private static void prepare(boolean quitAllowed) {
- if (sThreadLocal.get() != null) {
- throw new RuntimeException("Only one Looper may be created per thread");
- }
- sThreadLocal.set(new Looper(quitAllowed));
- }
可以看到,线程中的局部变量,是在线程中创建,然后调用ThreadLocal.set()方法设置进去的。
下面详细读一下ThreadLocal这个类,看看它是怎么实现的(jdk1.6的源码)。
从上图可以看到ThreadLocal有三个变(常)量,代码如下:
- /**
- * ThreadLocals rely on per-thread linear-probe hash maps attached
- * to each thread (Thread.threadLocals and
- * inheritableThreadLocals). The ThreadLocal objects act as keys,
- * searched via threadLocalHashCode. This is a custom hash code
- * (useful only within ThreadLocalMaps) that eliminates collisions
- * in the common case where consecutively constructed ThreadLocals
- * are used by the same threads, while remaining well-behaved in
- * less common cases.
- */
- private final int threadLocalHashCode = nextHashCode();
- /**
- * The next hash code to be given out. Updated atomically. Starts at
- * zero.
- */
- private static AtomicInteger nextHashCode =
- new AtomicInteger();
- /**
- * The difference between successively generated hash codes - turns
- * implicit sequential thread-local IDs into near-optimally spread
- * multiplicative hash values for power-of-two-sized tables.
- */
- private static final int HASH_INCREMENT = 0x61c88647;
其中HASH_INCREMENT 是一个常量,它表示连续的两个ThreadLocal实例的threadLocalHashCode值的增量。至于为什么用0x61c88647常量,我也没明白,只百度出一堆跟加密算法有关的内容。nextHashCode变量是AtomicInteger类型,AtomicInteger是一个提供原子操作的Integer类,简单而言就是不用Synchronized关键字也可以进行线程安全的加减操作。
当创建一个ThreadLocal对象时,会进行以下操作:
- public ThreadLocal() {
- }
- private final int threadLocalHashCode = nextHashCode();
我们再来看nextHashCode()这个方法,代码如下:
- /**
- * Returns the next hash code.
- */
- private static int nextHashCode() {
- return nextHashCode.getAndAdd(HASH_INCREMENT);
- }
它是返回一个hash code,同时对nextHashCode加上增量HASH_INCREMENT。也就是在初始化ThreadLocal的实例过程中,做的仅仅是将一个哈希值赋给实例的threadLocalHashCode,并生成下一个哈希值。而前面可以看到threadLocalHashCode是final的,在个类中,它用来区分不同的ThreadLocal对象。
在前面的图当中,我们可以看到在ThreadLocal里还有一个ThreadLocalMap的内部类,从类名来看应该是被设计来保存线程局部变量的Map,但是在ThreadLocal类或它的实例当中,并没有其他变量,那么通过ThreadLocal.set()放进去的值又是怎么保存的呢?
我们继续看ThreadLocal的set()和其他方法:
- /**
- * 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;
- }
- /**
- * Create the map associated with a ThreadLocal. Overridden in
- * InheritableThreadLocal.
- *
- * @param t the current thread
- * @param firstValue value for the initial entry of the map
- * @param map the map to store.
- */
- void createMap(Thread t, T firstValue) {
- t.threadLocals = new ThreadLocalMap(this, firstValue);
- }
从上面的代码可以看到,set()方法是调用ThreadLocalMap.set()方法保存到ThreadLocalMap实例当中,但是ThreadLocalMap实例却不是保存在Thread中,它通过getMap()方法去取,当取不到的时候就去创建,但是创建之后却是保存在Thread实例中。可以看到Thread.java的代码有如下声明:
- ThreadLocal.ThreadLocalMap threadLocals = null;
这种设计很巧妙,线程中有各自的map,而把ThreadLocal实例作为key,这样除了当线程销毁时相关的线程局部变量被销毁之外,还让性能提升了很多。
看完set()方法,再来看get()方法,它的代码如下:
- /**
- * Returns the value in the current thread's copy of this
- * thread-local variable. If the variable has no value for the
- * current thread, it is first initialized to the value returned
- * by an invocation of the {@link #initialValue} method.
- *
- * @return the current thread's value of this thread-local
- */
- public T get() {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null) {
- ThreadLocalMap.Entry e = map.getEntry(this);
- if (e != null)
- return (T)e.value;
- }
- return setInitialValue();
- }
- /**
- * Variant of set() to establish initialValue. Used instead
- * of set() in case user has overridden the set() method.
- *
- * @return the initial value
- */
- private T setInitialValue() {
- T value = initialValue();
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null)
- map.set(this, value);
- else
- createMap(t, value);
- return value;
- }
- /**
- * Returns the current thread's "initial value" for this
- * thread-local variable. This method will be invoked the first
- * time a thread accesses the variable with the {@link #get}
- * method, unless the thread previously invoked the {@link #set}
- * method, in which case the <tt>initialValue</tt> method will not
- * be invoked for the thread. Normally, this method is invoked at
- * most once per thread, but it may be invoked again in case of
- * subsequent invocations of {@link #remove} followed by {@link #get}.
- *
- * <p>This implementation simply returns <tt>null</tt>; if the
- * programmer desires thread-local variables to have an initial
- * value other than <tt>null</tt>, <tt>ThreadLocal</tt> must be
- * subclassed, and this method overridden. Typically, an
- * anonymous inner class will be used.
- *
- * @return the initial value for this thread-local
- */
- protected T initialValue() {
- return null;
- }
可以看到在get()方法中,是通过当前线程获取map,再从map当中取得对象。如果取不到(如map为null或取到的值为null),则调用setInitialValue()方法对其设置初始值。setInitialValue()方法会调用initialValue()方法得到一个初始值(默认为null),然后当Thread中的map不为空时,把初始值设置进去,否则为它创建一个ThreadLocalMap对象(但不会调用map的set方法保存这个初始值),最后返回这个初始值。
最后看remove()方法,它提供了移除此线程局部变量在当前进程的值。代码如下:
- /**
- * Removes the current thread's value for this thread-local
- * variable. If this thread-local variable is subsequently
- * {@linkplain #get read} by the current thread, its value will be
- * reinitialized by invoking its {@link #initialValue} method,
- * unless its value is {@linkplain #set set} by the current thread
- * in the interim. This may result in multiple invocations of the
- * <tt>initialValue</tt> method in the current thread.
- *
- * @since 1.5
- */
- public void remove() {
- ThreadLocalMap m = getMap(Thread.currentThread());
- if (m != null)
- m.remove(this);
- }
关于ThreadLocal就读到这里,有时间再写下它的内部类ThreadLocalMap。
参考文章:
lujh99. 正确理解ThreadLocal. http://www.iteye.com/topic/103804
- Java中的ThreadLocal示例.
- 理解java中的ThreadLocal
- java中的ThreadLocal
- Java中的ThreadLocal类
- Java中的ThreadLocal类
- Java中的ThreadLocal
- 理解java中的ThreadLocal
- Java多线程中的ThreadLocal
- java线程中的ThreadLocal
- 理解Java中的ThreadLocal
- 谈谈Java中的ThreadLocal
- 谈谈Java中的ThreadLocal
- Java中的ThreadLocal对象
- Java中的ThreadLocal
- Java中的ThreadLocal
- JAVA中的ThreadLocal
- 谈谈Java中的ThreadLocal
- 谈谈Java中的ThreadLocal
- 面试经典算法1之交换排序
- Android中全局Application的onCreate多次调用问题
- 发布软件
- 复杂网络分析库NetworkX学习笔记3:网络演化模型
- leetcode之Longest Substring Without Repeating Characters
- Java中的ThreadLocal
- Compiler
- shrink合并数据块--解决数据块碎片问题
- java调用c# com组件编码过程笔记
- IOS详解TableView——对话聊天布局的实现
- SQL Server 2008过期导致MSSQLSERVER服务无法启动
- PropertyGrid中的枚举显示为中文
- 复杂网络分析库NetworkX学习笔记(4):统计指标计算
- Linker (computing)