转载自:https://www.cnblogs.com/dreamroute/p/5034726.html

ThreadLocal翻译成中文比较准确的叫法应该是:线程局部变量。

  这个玩意有什么用处,或者说为什么要有这么一个东东?先解释一下,在并发编程的时候,成员变量如果不做任何处理其实是线程不安全的,各个线程都在操作同一个变量,显然是不行的,并且我们也知道volatile这个关键字也是不能保证线程安全的。那么在有一种情况之下,我们需要满足这样一个条件:变量是同一个,但是每个线程都使用同一个初始值,也就是使用同一个变量的一个新的副本。这种情况之下ThreadLocal就非常使用,比如说DAO的数据库连接,我们知道DAO是单例的,那么他的属性Connection就不是一个线程安全的变量。而我们每个线程都需要使用他,并且各自使用各自的。这种情况,ThreadLocal就比较好的解决了这个问题。

  我们从源码的角度来分析这个问题。

  首先定义一个ThreadLocal:

public final class ConnectionUtil {

    private ConnectionUtil() {
} private static final ThreadLocal<Connection> conn = new ThreadLocal<>(); public static Connection getConn() {
Connection con = conn.get();
if (con == null) {
try {
Class.forName("com.mysql.jdbc.Driver");
con = DriverManager.getConnection("url", "userName", "password");
conn.set(con);
} catch (ClassNotFoundException | SQLException e) {
// ...
}
}
return con;
} }

  这样子,都是用同一个连接,但是每个连接都是新的,是同一个连接的副本。

  那么实现机制是如何的呢?

  1、每个Thread对象内部都维护了一个ThreadLocalMap这样一个ThreadLocal的Map,可以存放若干个ThreadLocal。

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

  2、当我们在调用get()方法的时候,先获取当前线程,然后获取到当前线程的ThreadLocalMap对象,如果非空,那么取出ThreadLocal的value,否则进行初始化,初始化就是将initialValue的值set到ThreadLocal中。

1
2
3
4
5
6
7
8
9
10
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();
}

  3、当我们调用set()方法的时候,很常规,就是将值设置进ThreadLocal中。

  4、总结:当我们调用get方法的时候,其实每个当前线程中都有一个ThreadLocal。每次获取或者设置都是对该ThreadLocal进行的操作,是与其他线程分开的。

  5、应用场景:当很多线程需要多次使用同一个对象,并且需要该对象具有相同初始化值的时候最适合使用ThreadLocal。

  6、其实说再多也不如看一下源码来得清晰。如果要看源码,其中涉及到一个WeakReference和一个Map,这两个地方需要了解下,这两个东西分别是a.Java的弱引用,也就是GC的时候会销毁该引用所包裹(引用)的对象,这个threadLocal作为key可能被销毁,但是只要我们定义成他的类不卸载,tl这个强引用就始终引用着这个ThreadLocal的,永远不会被gc掉。b.和HashMap差不多。

  事实上,从本质来讲,就是每个线程都维护了一个map,而这个map的key就是threadLocal,而值就是我们set的那个值,每次线程在get的时候,都从自己的变量中取值,既然从自己的变量中取值,那肯定就不存在线程安全问题,总体来讲,ThreadLocal这个变量的状态根本没有发生变化,他仅仅是充当一个key的角色,另外提供给每一个线程一个初始值。如果允许的话,我们自己就能实现一个这样的功能,只不过恰好JDK就已经帮我们做了这个事情。

初识Java ThreadLocal的更多相关文章

  1. 初识Java

    Java是一种简单的.面向对象的.分布式的.解释的.安全的.可移植的.性能优异的多线程语言.它以极强的安全性.平台无关性.硬件结构无关性.语言简洁.面向对象的特点,在网络编程语言中占据了无可比拟的优势 ...

  2. SSH 框架学习之初识Java中的Action、Dao、Service、Model-收藏

    SSH 框架学习之初识Java中的Action.Dao.Service.Model-----------------------------学到就要查,自己动手动脑!!!   基础知识目前不够,有感性 ...

  3. Java 面向对象编程——第一章 初识Java

      第一章    初识Java 1.  什么是Java? Java是一种简单的.面向对象的.分布式的.解释的.安全的.可移植的.性能优异的多线程语言.它以其强安全性.平台无关性.硬件结构无关性.语言简 ...

  4. Personal Learning Path of Java——初识Java

    初识Java 在我个人看来,Java是一门高大上的面向编程语言,这也是Java吸引我的地方.在自学Java之前,我在学校大概学过了一些C语言的知识,在学校学的那点C语言纯属是拿来打基础用的,大概了解了 ...

  5. Java ThreadLocal的使用

    Java中的ThreadLocal类允许我们创建只能被同一个线程读写的变量.因此,如果一段代码含有一个ThreadLocal变量的引用,即使两个线程同时执行这段代码,它们也无法访问到对方的Thread ...

  6. 初识Java作业

    初识Java作业 一.    填空题 Java技术按照用途不同分为三大版本,分别是JavaSE.     javaEE       和JavaMe Java虚拟机就是一个虚拟的用于执行  .class ...

  7. Java学习笔记心得——初识Java

    初识Java 拿到这本厚厚的<Java学习笔记>,翻开目录:Java平台概论.从JDK到TDE.认识对象.封装.继承与多态...看着这些似懂非懂的术语名词,心里怀着些好奇与担忧,就这样我开 ...

  8. Java ThreadLocal 源代码分析

    Java ThreadLocal 之前在写SSM项目的时候使用过一个叫PageHelper的插件 可以自动完成分页而不用手动写SQL limit 用起来大概是这样的 最开始的时候觉得很困惑,因为直接使 ...

  9. 初识Java程序,编写简单代码?

    Dear All: 初识Java程序,编写简单代码? 首先小编在这里说下我们今天编写Java程序使用的是 eclipse 开发工具! 1.下载eclipse 官网地址:http://www.eclip ...

随机推荐

  1. eclipse 和 javaClass

    eclipse 如果设置为 Build automaticaly 会自动对当前的类进行编译,放在项目下的bin文件夹下. 1. 如果此Class有错,则编译后的Class不能用,里面仅仅写会抛出异常代 ...

  2. Qt 的事件

    一个事件由一个特定的QEvent子类来表示,如QMouseEvent.QKeyEvent 处理一个事件的方法: 方法一:重新实现部件的paintEvent.mousePressEvent等事件处理函数 ...

  3. delphi webbrowser 执行 js ---转

    EmbeddedWB1.OleObject.document.parentWindow.execScript(memo1.Text, 'javascript');

  4. 由于C++类库版本不同导致的OpenCV编译链接错误

    太长不看版:GCC4和GCC5使用的C++标准库下,string的名字不一样,导致链接错误. 之前在Ubuntu下使用OpenCV的时候一切正常.后来再次编译的时候,连接器提示有些库函数找不到: ma ...

  5. 常用类一一MATH类一一两个静态常量PI 和E,一些数学函数。

    package test; public class MathTest { public static void main(String[] args) { System.out.println(Ma ...

  6. redis安装和命令使用

    前言: redis是一个key-value的存储系统,value支持string.list.set.zset.hash五种类型,且支持数据的本地存储   一.安装redis 前提:linux下需要安装 ...

  7. httpclient4例子

    参考:http://hc.apache.org/httpclient-3.x/tutorial.html import org.apache.http.HttpEntity; import org.a ...

  8. 对于局部变量,text、ntext 和 image 数据类型无效

    开发存储过程时报如上错误.大多数人说用varchar(8000)代替text,但值我这里超过8000,不可取 解决: sql2005或以上版本支持新数据类型:varchar(max)nvarchar( ...

  9. 二叉查找树迭代器 · Binary Search Tree Iterator

    [抄题]: 设计实现一个带有下列属性的二叉查找树的迭代器: 元素按照递增的顺序被访问(比如中序遍历) next()和hasNext()的询问操作要求均摊时间复杂度是O(1) 对于下列二叉查找树,使用迭 ...

  10. 平衡二叉树Balanced Binary Tree

    [抄题]: Given a binary tree, determine if it is height-balanced. For this problem, a height-balanced b ...