欢迎点赞阅读,一同学习交流,有疑问请留言 。

GitHub上也有开源 JavaHouse 欢迎star

1 引入

在Java8里面,ThreadLocal 是一个泛型类。这个类可以提供线程变量。每个线程都有自己的变量。这意味着什么?每一个线程都有自己的资源,就像在现实生活中,每一个程序员都有自己的一个对象,不用去竞争,绝对的线程安全啊。那么 ThreadLocal 究竟怎么用呢?

2 类的说明

* This class provides thread-local variables.  These variables differ from
* their normal counterparts in that each thread that accesses one (via its
* {@code get} or {@code set} method) has its own, independently initialized
* copy of the variable. {@code 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).

这是 ThreadLocal 类上面的说明。大概意思是提供线程变量,通常是被 static fields 修饰。

3 创建他

创建 ThreadLocal 有两种方法,一种通过原始的无参构造器, 另一种是使用Java8的 lamaba 表达式。

3.1 无参构造器

源码

/**
* Creates a thread local variable.
* @see #withInitial(java.util.function.Supplier)
*/
public ThreadLocal() {
}

使用并初始化

private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue(){
return 1;
}
};

3.2 lamaba 表达式


/**
* Creates a thread local variable. The initial value of the variable is
* determined by invoking the {@code get} method on the {@code Supplier}.
*
* @param <S> the type of the thread local's value
* @param supplier the supplier to be used to determine the initial value
* @return a new thread local variable
* @throws NullPointerException if the specified supplier is null
* @since 1.8
*/
public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
return new SuppliedThreadLocal<>(supplier);
}

使用并初始化

private static final ThreadLocal<Integer> threadId = ThreadLocal.withInitial(() -> 1);

其实使用如果使用 IDEA 的话, 编译器也会提示可以有 lamaba。 但是看看里面的源码还是挺有意思的。

4 getter() 方法

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

这里可以看到 Thread.currentThread,获取了当前线程,还有 ThreadLocalMap 这个类,他是一个哈希结构(key-value)。getMap() 方法通过当前线程去获取他。 然后从再通过 this 关键字作为key,得到相应的值value。当然如果为空,就会返回初始化的值。

5 setter() 方法

什么情况下不会返回初始化的默认值呢,答案就是调用了setter() 方法。先看源码

/**
* 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);
}

大概意思就是将当前线程作为key, 要设的值作为 value 放在 ThreadLocalMap 这个哈希结构中。看到这里,就知道为什么说 ThreadLocal 可以提供线程变量了。他讲每个线程都分开存储,每个线程都有自己的独立资源,不存在资源共享的情况,所以线程安全。

6 内存泄漏

每个线程变量都放进一个 ThreadLocalMap 里面,不会有内存问题吗。我截取一部分源码

static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value; Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
} }

这里可以看到了 WeakReference 这个类,可以知道 ThreadLocalMap 类是一个弱引用,按道理来说,一般执行完线程,就会被虚拟机的垃圾回收机制回收了。但是,真的是这样的吗。如果是线程池环境里面,线程一直存在,那么 ThreadLocal 就会变成起强引用了,不能被回收了。所以有内存泄漏问题。

那现在就是 remove() 方法出场的时候了。按照字面意思,可以知道,该方法可以清掉线程变量资源,事实也是这样。所以,在程序的最后,最好调用一下 emove() 方法,防止内存泄漏。

参考

《实战Java高并发程序设计》

ThreadLocal 源码代

关注微信公众号,随时移动端阅读

ThreadLocal快速了解一下的更多相关文章

  1. ThreadLocal的应用与实现原理

    本文对ThreadLocal的分析基于JDK 8. 本文大纲 1. ThreadLocal快速上手 2. ThreadLocal应用场景 3. TheadLocal set与get方法简析 4. Th ...

  2. ThreadLocal 是什么鬼?用法、源码一锅端

    ThreadLocal 是一个老生常谈的问题,在源码学习以及实际项目研发中,往往都能见到它的踪影,用途比较广泛,所以有必要深入一番. 敢问,ThreadLocal 都用到了哪里?有没有运用它去解决过业 ...

  3. 【Java】ThreadLocal细节分析

    ThreadLocal通过中文解释就是线程本地变量,是线程的一个局部变量.根据哲学家黑格尔“的存在即合理”的说法,ThreadLocal的出现肯定是有它的意义,它的出现也是因为多线程的一个产物.Thr ...

  4. SpringMVC:学习笔记(1)——理解MVC及快速入门

    SprigMVC-理解MVC及快速入门 说明: 传统MVC-->JSPModel2-->Front Controller + Application Controller + Page C ...

  5. 阿里巴巴Java开发手册快速学习

    Java作为一门名副其实的工业级语言,语法友好,学习简单,大规模的应用给代码质量的管控带来了困难,特别是团队开发中,开发过程中的规范会直接影响最终项目的稳定性. 善医者“未有形而除之”,提高工程健壮性 ...

  6. Java核心编程快速学习

    Java核心编程部分的基础学习内容就不一一介绍了,本文的重点是JAVA中相对复杂的一些概念,主体内容如下图所示. 反射reflect是理解Java语言工作原理的基础,Java编译器首先需要将我们编写的 ...

  7. Mybatis第一篇【介绍、快速入门、工作流程】

    什么是MyBatis MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为 ...

  8. ThreadLocal源码分析:(一)set(T value)方法

    在ThreadLocal的get(),set()的时候都会清除线程ThreadLocalMap里所有key为null的value. 而ThreadLocal的remove()方法会先将Entry中对k ...

  9. 第一阶段——CentOS6_Python3.6.1笔记(尚学堂-Python基础快速入门)+ 【补充】麦子-Python程序入门与进阶

    虚拟机环境: 设置网络 .修改网络地址 .设置网卡为nat模式 .确保物理机启动dhcp.net服务 .编辑文件:vim /etc/sysconfig/network-scripts/ifcfg-et ...

随机推荐

  1. 全栈项目|小书架|服务器开发-Koa2 全局异常处理

    什么是异常 做开发的基本都知道异常,像Android开发中常见的ANR异常.空指针异常,服务器开发中经常遇到的异常404,500异常,还有一些其他常见的异常,具体可见HTTP状态码. 基本上这些异常可 ...

  2. 差异:后缀数组(wzz模板理解),单调栈

    因为涉及到对模板的理解,所以就着代码看会好一些. 让那些坚决不颓代码的人受委屈了. 我是对着wzz的板子默写的,可能不完全一样啊. 还有代码注释里都是我个人的理解,不保证正确,但欢迎指正. 可以有选择 ...

  3. day 1 堆 hash 线段树 树状数组 冰茶姬 字典树 二叉查找树

    来郑州的第二天,早上开始也没说什么就说了些注意安全,各种各样的注意安全... 冰茶姬: 原来再打食物链时看了一下冰茶姬,只注意了路径压缩,没想到还有什么按秩排序但确实快了不少... int find( ...

  4. npm start 的应用 改为forever 后台启动的方法记录

    记录日志的启动方式 forever start -l forever.log -o out.log -e err.log app.js 记录日志的启动方式 forever start -a app.j ...

  5. git 生成密匙时遇到报错 :Too many arguments.

    今天在我用新电脑想要向github上的远程库上传项目时,由于新电脑上的本地库没有与远程库建立连接,所以要在本地生成密匙添加到github上才可以上传,在我执行命令 ssh-keygen -t rsa- ...

  6. Linux命令实战(五)

    1.显示/etc目录下,以非字母开头,后面跟了一个字母以及其他任意长度字符的文件或目录. [qiuhom@test ~]$ls -d /etc/[^[:alpha:]][[:alpha:]]* ls: ...

  7. 【实战】如何通过html+css+mysql+php来快速的制作动态网页(以制作一个博客网站为列)

    一.开发环境的搭建 (1)apache+php+mysql环境搭建 因为要用apache来做服务器,mysql作为数据库来存储数据,php来写代码以此实现网页与数据库的交互数据,所以需要下载上述软件, ...

  8. Zabbix-(四)邮件、钉钉告警通知

    Zabbix-(四)邮件.钉钉告警通知 一.前言 在之前的文章里,通过Zabbix对主机的磁盘.CPU以及内存进行了监控,并在首页Dashboard里创建了监控图形,但是只有当我们登录到Zabbix后 ...

  9. springMVC-MyBatis-Mysql 环境下, 返回时间格式不是指定格式

    在数据库中的时间是: 比如: 2018-04-06:12;23:34. 但是mybatis查询出来以后是下面的格式. {"updatedTime":{"date" ...

  10. 在linux (centos)上使用puppeteer实现网页截图

    1.安装nodejs和npm # 下载解压 wget -c https://nodejs.org/dist/v8.9.1/node-v8.9.1-linux-x64.tar.xz tar -xvf n ...