文档章节

ThreadLocal源码笔记

JoeyXie
 JoeyXie
发布于 2016/02/02 01:56
字数 1125
阅读 30
收藏 0
点赞 1
评论 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关键字来加锁保证线程安全的。

© 著作权归作者所有

共有 人打赏支持
JoeyXie
粉丝 0
博文 32
码字总数 33781
作品 0
深圳
后端工程师
111 多线程JUC包下代码分析

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

素雷
2017/10/31
0
0
sharding-jdbc源码分析—准备工作

原文作者:阿飞Javaer 原文链接:https://www.jianshu.com/p/7831817c1da8 接下来对sharding-jdbc源码的分析基于tag为源码,根据sharding-jdbc Features深入学习sharding-jdbc的几个主要特性...

飞哥-Javaer
05/03
0
0
sharding-jdbc源码解析全集

本文转自“天河聊技术”微信公众号 sharding-jdbc源码解析之词法解析 sharding源码解析之api分析 sharding-jdbc源码解析之spring集成 sharding-jdbc源码解析之spring集成分片构造实现 shardi...

天河2018
05/03
0
0
java基础io流——配角也风流(不求甚解)

本章简单介绍几个常见的io流派生。 1:数据操作流(操作基本类型数据的流)(理解) 代码示例: 2:内存操作流(理解) 用于处理临时存储信息的,程序结束,数据就从内存中消失。 代码示例: 注:查看...

潇潇漓燃
05/30
0
0
升级到JDK9的一个BUG,你了解吗

概述 前几天在一个群里看到一个朋友发了一个demo,说是JDK的bug,昨天在JVM的一个群里又有朋友发了,觉得挺有意思,分享给大家,希望大家升级JDK的版本的时候注意下是否存在这样的代码,如果...

你假笨
06/06
0
0
读书笔记之《Java并发编程的艺术》-并发编程容器和框架(重要)

读书笔记部分内容来源书出版书,版权归本书作者,如有错误,请指正。 欢迎star、fork,读书笔记系列会同步更新 git https://github.com/xuminwlt/j360-jdk module j360-jdk-thread/me.j360....

Hi徐敏
2015/11/11
0
1
读书笔记之《Java并发编程的艺术》-并发编程基础

读书笔记部分内容来源书出版书,版权归本书作者,如有错误,请指正。 欢迎star、fork,读书笔记系列会同步更新 git https://github.com/xuminwlt/j360-jdk module j360-jdk-thread/me.j360....

Hi徐敏
2015/11/11
0
8
Java培训实战教程之Java基础知识精华部分(一)(二)(三)

Java培训实战教程之Java基础知识精华部分(一)_java概述 =============================================================================Java培训实战教程之Java基础知识精华部分(一)_java概...

黑泽明军
04/13
0
0
Cocos2dx游戏开发系列笔记8:开搞一个射击游戏《战神传说》//就个打飞机的

又是一个愉快的夜晚 还是那张长长的工作桌 哲哲在左边做一个香云纱手包 骨头在这里噼里啪啦的解刨 pad里放着《赢在中国》 桌子中间各种饮料、奶、水果... 进击吧~骨头~ 首先感谢Android技术...

懒骨头
2013/12/09
0
0
读书笔记之《Java并发编程的艺术》-线程池和Executor的子孙们

读书笔记部分内容来源书出版书,版权归本书作者,如有错误,请指正。 欢迎star、fork,读书笔记系列会同步更新 git https://github.com/xuminwlt/j360-jdk module j360-jdk-thread/me.j360....

Hi徐敏
2015/11/11
0
1

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Confluence 6 配置时间和日期格式

你可以修改你 Confluence 为用户显示的时期和时间格式。设置的句法使用的是 SimpleDateFormat class,请参考 Java SimpleDateFormat 文档中的内容来设置日期和时间格式。 有下面 3 个时间和日...

honeymose
11分钟前
0
0
php seralize unserialize

关于PHP 序列化(serialize)和反序列化(unserialize)出现错误(Error at offset)的解决办法。 首先我们分析一下为什么会出现这个错误: 编码问题 UTF-8: ANSI: 我发现在我的机器上边编码改...

yeahlife
17分钟前
0
0
七、JSP九大内置对象和四个作用域

九大内置对象: request:类型是HttpServletRequest,和Servlet里的HttpServletRequest一模一样。 response:类型是HttpServletResponse,和Servlet里的HttpServletResponse一模一样。JSP里基...

Wakeeee_
21分钟前
0
0
第十四章NFS服务搭建与配置

14.1 NFS介绍 NFS介绍 NFS是Network File System的缩写;这个文件系统是基于网路层面,通过网络层面实现数据同步 NFS最早由Sun公司开发,分2,3,4三个版本,2和3由Sun起草开发,4.0开始Netap...

Linux学习笔记
53分钟前
0
0
双向认证-nginx

1、设置容器 docker run -it --name nginx-test2 -v /home/nginx:/apps -v /home/nginx/conf/nginx.conf:/etc/nginx/nginx.conf:ro -p 8183:80 -p 7443:443 -d nginx:stable 2、修改nginx配......

hotsmile
53分钟前
0
0
深入了解 Java 自动内存管理机制及性能优化

一图带你看完本文 一、运行时数据区域 首先来看看Java虚拟机所管理的内存包括哪些区域,就像我们要了解一个房子,我们得先知道这个房子大体构造。根据《Java虚拟机规范(Java SE 7 版)》的规...

Java大蜗牛
55分钟前
4
0
SpringBoot | 第六章:常用注解介绍及简单使用

前言 之前几个章节,大部分都是算介绍springboot的一些外围配置,比如日志 配置等。这章节开始,开始总结一些关于springboot的综合开发的知识点。由于SpringBoot本身是基于Spring和SpringMvc...

oKong
56分钟前
12
0
云数据库架构演进与实践

如今,大型企业如金融企业和银行等,在下一代的微服务架构转型要求下,需要基础软件和数据平台能够实现原生的云化,以满足微服务架构的需求。 微服务,也就是一种面向服务的,有特定边界的松...

巨杉数据库
57分钟前
0
0
Linux系统梳理---系统搭建(一):jdk卸载与安装

1.去官网下载符合Linux版本的jdk,暂用jdk-8u171-linux-x64.rpm 2.登陆Linux,进入usr目录,创建java目录(方便管理,可以其他位置):mkdir java 3.上传下载的jdk包至Linux服务器,使用rz指令(sz f...

勤奋的蚂蚁
今天
0
0
Linux Kernel 4.16 系列停止维护,用户应升级至 4.17

知名 Linux 内核维护人员兼开发人员 Greg Kroah-Hartman 近日在发布 4.16.18 版本的同时,宣布这是 4.16 系列的最后一个维护版本,强烈建议用户立即升级至 4.17 系列。 Linux 4.16 于 2018 年...

六库科技
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部