文档章节

ThreadLocal源码笔记

JoeyXie
 JoeyXie
发布于 2016/02/02 01:56
字数 1125
阅读 31
收藏 0

        最近用到了ThreadLocal,所以看了一下JDK的源码。ThreadLocal是以空间换时间的典型,因为它避免了使用synchronized关键字来对变量加锁,从而节约了很多时间。ThreadLocal是为每个线程创建一个变量的副本,所以每个线程都可以访问这个副本的内容并修改,彼此之间的副本不会相互影响,因此看似会修改同一个变量,但实际上线程间修改的都是线程本地变量。例如下面这个数据库连接类:

public class DatabaseUtil {
    private static final ThreadLocal<Connection> CON_HOLDER = new ThreadLocal<Connection>();

    public static Connection getConn() {
        Connection con = CON_HOLDER.get();
        if(null == con) {
            try {
                con = DriverManager.getConnection(url, username, pwd);
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                CON_HOLDER.set(con);
            }
        }
        return con;
    }

    // 其它函数
    ......
}

       我们可以在Servlet中调用getConn()方法去获取一个连接,然后执行相应的数据库操作(暂且忽略这种做法的不规范性,先讨论多线程访问的问题)。Servlet在Java web中是单实例多线程的执行模式,这也是为什么我们一般不在Servlet类中定义成员变量的原因,就是为了避免线程不安全。那么当多个请求到来时,每个请求都是在一个线程里面来访问servlet的方法的,因此看上去每个线程获取到的连接都是同一个实例(getConn方法类似单例模式)。但实际上并不是这样,每个请求都会在它的线程中获取到不同的Connection连接,这就是ThreadLocal的作用。首先看ThreadLocal的get()方法的源码:

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();
}

      首先它获取了当前正在运行的线程,getMap()方法其实就是获取Thread中的ThreadLocalMap对象,并没有什么特别的。然后从ThreadLocalMap中根据当前ThreadLocal对象作为key去获取对应的值,接着返回。Thread类中有以下两个成员值得注意:

/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

/*
 * InheritableThreadLocal values pertaining to this thread. This map is
 * maintained by the InheritableThreadLocal class.
 */
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

     刚才的getMap()方法其实返回的就是threadLocal对象,现在Thread、ThreadLocal和ThreadLocalMap对象混在一起有点乱,下面来理清楚它们的关系:

     1. 每个Thread都有一个ThreadLocalMap对象threadLocals作为它的成员变量,它的key是ThreadLocal对象,值就是ThreadLocal中保存的值,如果一个线程访问了多个ThreadLocal对象,则ThreadLocalMap中就会有多个映射关系

     2. 每一个ThreadLocal对象都有自己唯一的hash值,这个值就是用于在ThreadLocalMap中查找对应的value的,所以以ThreadLocal对象为key就可以找到对应的线程本地的值

     3. ThreadLocalMap是ThreadLocal的一个静态内部类(就把它想象成一个普通的类就好了,只不过访问它需要通过ThreadLocal来访问),保存了很多个entry,每个entry都是一个ThreadLocal与它的值的映射关系

      说了这么多,还是贴一张图比较直观:

    从上图可以看到,每个线程都拥有一个ThreadLocalMap对象,它会保存ThreadLocal和value的映射关系,ThreadLocal就相当于一个中间人,每次线程方法它的get或set方法时,它都会首先获取当前运行的线程,然后获取线程中的ThreadLocalMap对象,由于每个ThreadLocal对象都有唯一hash值,所以以ThreadLocal为key可以在当前线程中找到它对应的值。每个线程都维护自己的ThreadLocalMap对象,这个就是真正的“副本”,通过ThreadLocal这个中介就可以保证成员变量的线程安全。每个线程都有自己的映射(即ThreadLocalMap),当线程第一次设置ThreadLocal的值的时候,其实就是在自己的map中添加一条访问的ThreadLocal对象和值的映射,创建副本,然后就可以根据该ThreadLocal对象(也就是它的hashcode)找到对应的值,而不需要访问共享的变量。

    ThreadLocal没有使用synchronized关键字,而是在Thread中维护一份本地的副本,如果每个线程都希望独享自己的成员变量时,ThreadLocal是一个好选择,但如果确实需要多线程共享一个成员变量时,那么还是需要使用synchronized关键字来加锁保证线程安全的。

© 著作权归作者所有

共有 人打赏支持
上一篇: AWS建站总结
下一篇: CGlib使用笔记
JoeyXie
粉丝 0
博文 34
码字总数 34321
作品 0
深圳
后端工程师
私信 提问
111 多线程JUC包下代码分析

Java多线程系列目录(共43篇) AtomicLongFieldUpdater:通过反射+CAS实现对传入对象的指定long字段实现类似AtomicLong的操作 http://www.cnblogs.com/skywang12345/p/javathreadscategory.ht...

素雷
2017/10/31
0
0
Android--面试中遇到的问题总结(三)

《Android 开发工程师面试指南 LearningNotes 》,作者是陶程,由梁观全贡献部分。大家可以去知乎关注这两位用心的少年。这份指南包含了大部分Android开发的基础、进阶知识,不仅可以帮助准备...

sealin
2017/02/22
0
0
一份关于 Java、Kotlin 与 Android 的学习笔记

JavaKotlinAndroidLearn 这是一份关于 Java 、Kotlin 、Android 的学习笔记,既包含对基础知识点的介绍,也包含对一些重要知识点的源码解析,笔记的大纲如下所示: Java 重拾Java(0)-基础知...

叶应是叶
08/08
0
0
全新互联网Java后端开发技术思维导图

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/t4i2b10X4c22nF6A/article/details/82714797 图文里的技术如何学习,有没有免费资料? 对Java的技术,架构技术...

JAVA高级架构v
09/15
0
0
BAT等大厂Android面试书单和知识点清单

java是Android开发的基础,在BAT的初面中,会涉及到比较多的java基础知识,所以比较重要,下面我介绍的书籍内容是由浅到深。 1.Thinking in java:这本书被称为Java的三大圣经之一,虽然书比...

android自学
07/25
0
0

没有更多内容

加载失败,请刷新页面

加载更多

大数据教程(8.3)wordcount程序运行过程的解析

上一篇博客分享了wordcount的源码编写、原理实现,本节将对wordcount在hadoop内部运行过程进行解析。 运行流程图如下: 上图中说明:mr appmaster启动后,会根据任务分配规则进行任务的启动,...

em_aaron
24分钟前
1
0
Zookeeper安装(LINUX环境)

一、上传安装包 二、解压安装包 mkdir -p /usr/lib/jvm tar -zxvf zookeeper-3.4.10.tar.gz -C /usr/lib/jvm 三、修改配置文件 复制zoo_sample.cfg改名zoo.cfg 给zoo.cfg赋权 chmod 777 zoo.......

开源中国首席碉堡了
47分钟前
2
0
【面试必问】支撑百万并发的"IO多路复用"技术你了解吗?

多路复用其实并不是什么新技术,它的作用是在一个通讯连接的基础上可以同时进行多个请求响应处理。对于网络通讯来其实不存在这一说法,因为网络层面只负责数据传输;由于上层应用协议的制订问...

Java干货分享
49分钟前
3
0
Rotate Array(leetCode189)

Given an array, rotate the array to the right by k steps, where k is non-negative. Example 1: Input: [1,2,3,4,5,6,7] and k = 3Output: [5,6,7,1,2,3,4]Explanation:rotate 1 s......

woshixin
今天
1
0
给女朋友讲解什么是Optional【JDK 8特性】

前言 只有光头才能变强 前两天带女朋友去图书馆了,随手就给她来了一本《与孩子一起学编程》的书,于是今天就给女朋友讲解一下什么是Optional类。 至于她能不能看懂,那肯定是看不懂的。(学到...

Java3y
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部