在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. MVC ValidationAttribute 验证一个字段必须大于另一个字段

    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] pu ...

  2. Hadoop和YARN :map+shuffle+reduce走读

    今天做了一个hadoop分享,总结下来,包括mapreduce,及shuffle深度讲解,还有YARN框架的详细说明等. v\:* {behavior:url(#default#VML);} o\:* ...

  3. PHP get_class_vars 和 (array)

    <?php class Girl { public $id = 1; public $name = 'zhy'; } $start = microtime(TRUE); var_dump(get ...

  4. [Luogu2824] [HEOI2016/TJOI2016]排序

    题目描述 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他.这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行 ...

  5. gitlab 提交

    gitlab 提交 Git global setup git config --global user.name "lial" git config --global user.e ...

  6. 安装Go语言及搭建Go语言开发环境

    一步一步,从零搭建Go语言开发环境. 安装Go语言及搭建Go语言开发环境 下载 下载地址 Go官网下载地址:https://golang.org/dl/ Go官方镜像站(推荐):https://gol ...

  7. 《Java并发编程实战》读书笔记-第4章 对象的组合

    设计线程安全的类 三个基本要素: 找出构成对象状态的所有变量 找出约束状态变量的不变性条件 建立对象状态的并发访问管理策略 实例封闭 将数据封装在对象内部,可以将数据的访问限制在对象的方法上,从而更容 ...

  8. (转)python中@property详解

    转:https://www.cnblogs.com/zhangfengxian/p/10199935.html

  9. 向net core 3.0进击——项目发布与部署

    目录 前言 发布 测试 小结 前言 在经历过好多折腾后,总算是把部署走通了一遍,之前只是简单创建个工程在linux下部署,后来一直将这件事搁置,直到最近刚好团队入手一个小服务器,很显然是linux的, ...

  10. excel函数--笔记

    1: =DATE(MID(A1,1,4),MID(A1,5,2),MID(A1,7,2)) 字符串返回日期类型 2:  WEEKDAY(serial_number,return_type) ▪ ser ...