`
貌似掉线
  • 浏览: 260110 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

Java中的ThreadLocal源码解析(上)

阅读更多
这一篇之所以讲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实现代码不一样,但用法是一样的),代码如下:
    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
  • 大小: 16 KB
0
2
分享到:
评论

相关推荐

    threadlocal源码.jpg

    threadlocal源码解析

    Java源码解析ThreadLocal及使用场景

    Java源码解析ThreadLocal及使用场景 ThreadLocal是Java中一个非常重要的类,它在多线程环境下经常使用,用于提供线程本地变量。这些变量使每个线程都有自己的一份拷贝,使得多个线程可以独立地使用变量,不会彼此...

    疯狂Java实战演义【书+源码】(疯狂Java讲义课后习题项目)

    书中的源码是作者精心设计和编写的,提供了详尽的示例,以便读者能够动手实践,加深对Java语言的理解。 在Java编程领域,理解和掌握核心概念至关重要。首先,Java的面向对象特性是其最基础也最关键的部分。包括类、...

    ThreadPoolExecutor源码解析.pdf

    《ThreadPoolExecutor源码解析》 ThreadPoolExecutor是Java并发编程中重要的组件,它是ExecutorService接口的实现,用于管理和调度线程的执行。理解其源码有助于我们更好地控制并发环境下的任务执行,提高系统的...

    使用ThreadLocal管理“session”数据

    5. **源码解析** 要深入理解ThreadLocal的工作原理,需要查看其源码。ThreadLocal内部使用了一个ThreadLocalMap,它是一个基于ThreadLocal实例作为键,值为用户存储对象的弱引用表。每个线程都有一个这样的...

    threadLocal

    ThreadLocal是Java编程语言中的一个线程局部变量类,它为每个线程提供了一个独立的变量副本,使得每个线程可以独立地改变自己的副本,而不会影响其他线程所对应的副本。这个特性在多线程环境下处理并发问题时非常...

    java源码笔记

    5. **Java源码解析** - 核心库分析:深入JDK源码,如Collections、Stream、反射等模块,了解其内部实现机制。 - 设计模式:学习Java源码中常见的设计模式,如单例、工厂、观察者等,提升代码质量。 - 内存模型:...

    Android 详解ThreadLocal及InheritableThreadLocal

    在Android SDK 23的源码中,ThreadLocal的实现可能与Java标准库有所不同,这主要是为了适应Android平台的优化和限制。例如,Android可能使用了特定的内存管理策略或者线程局部存储实现。 总的来说,ThreadLocal和...

    马士兵 高并发 java架构 预习课 源码

    《马士兵高并发Java架构预习课:源码解析》 在Java开发中,高并发是性能优化和系统设计的关键所在。本课程基于马士兵老师的java高并发编程公开课,深入探讨了多线程在实际应用中的重要性及其使用技巧。通过对源码的...

    InheritableThreadLocal & ThreadLocal

    在Java编程中,线程局部变量(ThreadLocal)和可继承线程局部变量(InheritableThreadLocal)是两种非常重要的工具,它们允许我们在多线程环境中创建独立于线程的局部变量,确保每个线程拥有自己的变量副本,避免了...

    深入浅出ReentrantReadWriteLock源码解析.docx

    【深入浅出ReentrantReadWriteLock源码解析】 ReentrantReadWriteLock是Java并发包中的一个核心类,它提供了读写锁的实现,使得多个线程可以并发读取共享资源,但写操作是互斥的,即同一时间只能有一个线程进行写...

    Java多线程源码笔记.pdf

    11. ThreadLocal:ThreadLocal为每个线程提供了一个独立的变量副本,各个线程修改自己的副本不会影响其他线程,常用于解决线程安全问题,例如在Servlet容器中,每个请求对应一个线程,ThreadLocal可以用于存储请求...

    华为上机题 java版 源码

    【华为上机题 Java版 源码】深入解析 华为作为全球领先的ICT(信息与通信)解决方案提供商,其招聘过程中的技术考核自然备受关注。Java作为一种广泛应用的编程语言,对于软件开发工程师而言,掌握扎实的Java技能是...

    java源码解读-JavaSource:Java源码解读

    在Java编程语言的世界里,源码解读是提升技术深度、理解内部机制的关键步骤。"JavaSource:Java源码解读"项目旨在帮助开发者深入探索Java的内部...对于任何想要成为Java专家的人来说,深入源码解析都是必不可少的一环。

    JDK10(JDK10底层C++源码及hotspot虚拟机源码)

    **JDK10与OpenJDK10源码解析** JDK10是Java开发工具包的第十个主要版本,它的发布标志着Java平台的持续进化。这个版本引入了许多新的特性和改进,旨在提升性能、易用性和开发者的生产力。本文将深入探讨JDK10的底层...

    Java并发编程原理与实战

    CountDownLatch,CyclicBarrier,Semaphore源码解析.mp4 提前完成任务之FutureTask使用.mp4 Future设计模式实现(实现类似于JDK提供的Future).mp4 Future源码解读.mp4 ForkJoin框架详解.mp4 同步容器与并发容器.mp4 ...

    Java中对日期的常用处理(转)

    在Java编程语言中,日期和时间的处理是常见的任务,特别是在开发业务系统或者数据分析时。本文将基于标题“Java中对日期的常用处理(转)”来深入探讨Java中的日期处理,结合`DateUtil.java`这个文件名,我们可以...

    java源码解读-ITG-JavaBook01:Java面试高频源码解读

    如ArrayList、LinkedList、HashMap、HashSet等的内部实现原理,比如它们的扩容策略、线程安全问题以及各种操作的时间复杂度分析,这些都是源码解析的重点。 三、并发编程 Java并发编程是高级面试的重要部分,涉及到...

    70套Java项目案例(附源码教程)

    10. **源码解析**:每个项目都附带源码,通过阅读和分析源码,学习者能深入理解项目的实现细节和设计思路。 通过这70套项目案例的学习,你可以逐步提升自己的编程能力,不仅能够熟练掌握Java语法,还能了解软件开发...

    java面试题及技巧4

    │ 164个完整Java代码.zip │ J2EE综合--Struts常见错误的全面汇总.txt │ java程序员面试资料.zip │ JAVA笔试题(上海释锐).pdf │ MIME简介.txt │ SCJP试题详解.pdf │ SQL面试题_心灵深处.htm │ Struts+...

Global site tag (gtag.js) - Google Analytics