在activeJDBC框架内部的实现中看到了 ThreadLocal 这个类,记录下了每个线程独有的连接

private static final ThreadLocal<HashMap<String, Connection>> connectionsTL = new ThreadLocal<>();

感觉是个知识点,就打开源码看看了。先看一下源码里的解释

This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).

这个鸟文,瞎翻译一下,就是:

这个类提供了供线程专享的变量。这些变量不同与其它普通的变量,它是每个线程都有一个自己的独立初始化的变量(通过get和set方法实现)。这个类的实例常用于类的私有静态字段,以实现每个线程都有自己的状态(例如userId,事务ID等)。

先跑一下用法吧,

package com.test.threadlocal;

public class TestController {

	private static int index = 0;
private static String str = "这个字符串是每个线程共享的";
// 这个变量,看似是一个类的静态属性,实则是每个线程有自己独有的区域
private static ThreadLocal<String> threadStr = new ThreadLocal<String>() {
@Override
protected String initialValue() {
return "main线程专享";
}
}; public static void main(String[] args) throws InterruptedException {
for(int i = 0; i < 3; i++) {
Thread t = new MyThread();
t.start();
t.join();
} System.out.println(str);
System.out.println(threadStr.get());
} static class MyThread extends Thread{ @Override
public void run() {
index++;
str = "第" + index + "个str";
threadStr.set("第" + index + "个threadStr");
} } }

这个例子中,从str和threadStr变量的打印结果可以看出来。str被所有的线程读和写,threadStr在每个线程内部开辟了一块线程专享的区域。接下来,我们看一下具体实现。

先看一下构造函数

     /**
* 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();
/**
* Creates a thread local variable.
* @see #withInitial(java.util.function.Supplier)
*/
public ThreadLocal() {
}

构造函数是空的,但是,该类有一个私有整型常量threadLocalHashCodenextHashCode()方法我们就不看了,省的一如源码深似海。看鸟文的话,大概就是每new一个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);
}

容易看出,这个方法设置每个线程自己的value,相当于当前线程是key,然后得出一个ThreadLocalMap。显然,这个map用来保存线程内部的值,既然是map当然每个线程可以保存多个数值了,该map的value我们猜一下就是我要保存的具体的值,估计是用Object类声明的。那key是什么呢?我们看下ThreadLocalMap类的构造方法。

/**
* The table, resized as necessary.
* table.length MUST always be a power of two.
*/
private Entry[] table; /**
* Construct a new map initially containing (firstKey, firstValue).
* ThreadLocalMaps are constructed lazily, so we only create
* one when we have at least one entry to put in it.
*/
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}

我的天!这个类没有继承我们想象中的HashMap,或者是ConcurrentMap,但是看过,Map的内部实现的同学应该可以发现,这个Map的实现和HashMap的实现简直就是小巫见大巫,有没有。它在构造函数中做了如下几步:

  1. 初始化一个大小为16的Entry数组
  2. 通过上面说过的很迷的HashCode散列值与15取模得到将要存储在数组中的索引值
  3. 构造Entry,然后保存进去
  4. 长度设置为1
  5. 设置要扩容的限制大小为16的2/3

我们看到这个不就是用数组实现的Map嘛,看过HashMap实现的我们,觉得洒洒水啦。

Map的set和get方法就不分析了。ThreadLocal的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) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}

可见,是获取到当前线程,用作key获取到Map,然后用当前this获取到Entry实体。最后当然获取到了存储的value。

我编码,我快乐~

本文由博客一文多发平台 OpenWrite 发布!

我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=19l4zaaz7g6ct

ThreadLocal<T> 源码解析的更多相关文章

  1. Java 8 ThreadLocal 源码解析

    Java 中的 ThreadLocal是线程内的局部变量, 它为每个线程保存变量的一个副本.ThreadLocal 对象可以在多个线程中共享, 但每个线程只能读写其中自己的副本. 目录: 代码示例 源 ...

  2. Java ThreadLocal 的使用与源码解析

    GitHub Page: http://blog.cloudli.top/posts/Java-ThreadLocal-的使用与源码解析/ ThreadLocal 主要解决的是每个线程绑定自己的值,可 ...

  3. ThreadLocal源码解析-Java8

    目录 一.ThreadLocal介绍 1.1 ThreadLocal的功能 1.2 ThreadLocal使用示例 二.源码分析-ThreadLocal 2.1 ThreadLocal的类层级关系 2 ...

  4. EventBus源码解析 源码阅读记录

    EventBus源码阅读记录 repo地址: greenrobot/EventBus EventBus的构造 双重加锁的单例. static volatile EventBus defaultInst ...

  5. EventBus3.0源码解析

    本文主要介绍EventBus3.0的源码 EventBus是一个Android事件发布/订阅框架,通过解耦发布者和订阅者简化 Android 事件传递. EventBus使用简单,并将事件发布和订阅充 ...

  6. Android Handler机制(三)----Looper源码解析

    一.Looper Looper对象,顾名思义,直译过来就是循环的意思,从MessageQueue中不断取出message. Class used to run a message loop for a ...

  7. EventBus (三) 源码解析 带你深入理解EventBus

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40920453,本文出自:[张鸿洋的博客] 上一篇带大家初步了解了EventBus ...

  8. Android EventBus源码解析 带你深入理解EventBus

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40920453,本文出自:[张鸿洋的博客] 上一篇带大家初步了解了EventBus ...

  9. Flume-ng源码解析之Channel组件

    如果还没看过Flume-ng源码解析之启动流程,可以点击Flume-ng源码解析之启动流程 查看 1 接口介绍 组件的分析顺序是按照上一篇中启动顺序来分析的,首先是Channel,然后是Sink,最后 ...

随机推荐

  1. [Mathematics][MIT 18.02]Detailed discussions about 2-D and 3-D integral and their connections

    Since it is just a sort of discussion, I will just give the formula and condition without proving th ...

  2. Web渗透之mssql差异备份getshell

    简介 差异备份数据库得到webshell.在sql server 里dbo和sa权限都有备份数据库权限,我们可以把数据库备份称asp文件,这样我们就可以通过mssqlserver的备份数据库功能生成一 ...

  3. 常用windows命令

    目录 本教程概述 用到的工具 标签 简介 1.cmd的一些规则 2.cd切换目录命令 3.dir显示目录命令 4.type显示文本内容 5.del 删除文件 6.查看IP地址 7.net 命令 8.n ...

  4. 安装VMware Tools显示灰色正确解决办法

    首先问题如下: 解决办法如下:1.关闭虚拟机: 2.在虚拟机设置分别设置CD/DVD.CD/DVD2和软盘为自动检测三个步骤: 3.再重启虚拟机,灰色字即点亮. 大功告成,如果解决了你的问题,点个赞鼓 ...

  5. ‎Cocos2d-x 学习笔记(11.10) Spawn

    Spawn让多个action同时执行. Spawn有多种不同的create方法,最终都调用了createWithTwoActions(FiniteTimeAction *action1, Finite ...

  6. 算法随笔-二叉树遍历的N种姿势

    最近在练习用Python刷算法,leetcode上刷了快300题.一开始怀疑自己根本不会写代码,现在觉得会写一点点了,痛苦又充实的刷题历程.对我这种半路出家的人而言,收获真的很大. 今天就从二叉树遍历 ...

  7. Ubuntu 14.04风扇不停转,风扇狂转 的解决办法,亲测有效。

    Ubuntu 14.04风扇不停转,风扇狂转 的解决办法,亲测有效. 原文  http://ubuntuforums.org/showthread.php?t=2218367 楼主说:ubuntu 1 ...

  8. HTTP 结构详解

    转至 :https://blog.csdn.net/u010256388/article/details/68491509?utm_source=copy   引用 学习Web开发不好好学习HTTP报 ...

  9. 数据结构(四十五)选择排序(1.直接选择排序(O(n²))2.堆排序(O(nlogn)))

    一.选择排序的定义 选择排序的基本思想是:每次从待排序的数据元素集合中选取最小(或最大)的数据元素放到数据元素集合的最前(或最后),数据元素集合不断缩小,当数据元素集合为空时排序过程结束.常用的选择排 ...

  10. fenby C语言 P26

    指针 地址 类型+“*”+变量名=指针变量 int *p1;//指向整型变量的指针变量p1 float *p2;//指向浮点型变量的指针变量p2 char *p3;//指向字符型变量的指针变量p3 “ ...