相信很多人知道ThreadLocal是针对每个线程的,但是其中的原理相信大家不是很清楚,那咱们就一块看一下源码。

  首先,我们先看看它的set方法。非常简单,从当前Thread中获取map。那么这个getMap方法是什么样的呢?咱们继续看。

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

  从当前线程中去获取单钱线程的threadLocals. 继续跟进t.threadLocals。

 /**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}

这个threadLocals定义在Thread里边,也就是每个线程里边维护者自己的threadLocals,这样使得每个线程之间独立了。思路再次回头上边的set方法上,如果map不为null, 那么重新进行set操作,如果为空,则需要创建这个ThreadLocalMap。咱们看如何进行的创建。

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

以下为创建的过程,在ThreadLocalMap中有一个静态内部类,静态内部类和普通的类是一样的,静态属性和方法和静态内部类不一样,这一点大家要注意。首先根据当前的ThreadLocal的实例获取当前的hashCode然后再与(初始容量-1)结果为7,Entry是ThreadLocalMap的静态内部类,类型为弱引用,也就是说,如果里边的key为null时,该value就是过期的value, 后续将会被擦除。


/**
* Create the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the map
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}

/**
* 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);
} /**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value; Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}

  刚才是如果map为空的时候,重新创建了一个ThreadLocalMap赋值给了当前的Thread, 如果当前thread的map不为空时,那么就需要获取当前的threadLocalMap,然后重新将值set上。

  这块是它的set方法,那么咱们看一个下get方法,获取当前的thread,并且从thread中获取threadLocalMap,然后取出其中的value,如果没有找到这个map的话,初始化一个key为ThreadLocal的实例放入到当前的thread中的ThreadLocalMap中,并且将value设置为null。最后将null返回给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();
}

  这块内容大致的方法基本上就是这么多,那么我们在什么场景下使用呢?

  一般在web应用中context可以使用。比如SimpleDateFormat是非线程安全的,那么我们就可以借助这个类去进行处理,为了显示不同的格式。我自己写了一个例子如下:

package com.hqs;

import java.text.SimpleDateFormat;
import java.util.Date; /**
* Created by huangqingshi on 2018/1/14.
*/
public class ThreadLocalExample {
//SimpleDateFormat 不是线程安全的
private static final ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat();
}
}; public static void main(String[] args) {
SimpleDateFormat sdf = threadLocal.get();
sdf.applyPattern("yy/MM/dd");
System.out.println(sdf.format(new Date()));
sdf.applyPattern("MM/dd/yyyy");
System.out.println(sdf.format(new Date()));
} }

18/01/14
01/14/2018


Process finished with exit code 0

  使用这个ThreadLocal的时候,需要注意一些内容:

  如果定义一些大量的线程变量的,可能会出现不能创建线程的异常,因为一些web server针对线程会定义线程池,当使用ThreadLocal创建了很多线程,处理的时间比较久,那么这些线程不会释放,结果会导致栈溢出,程序没有响应。此时需要注意使用的时候,尽可能让当前线程处理完成。

  还有可能会因此出现Perm溢出,比如ThreadLocal里边装入了一些静态的类,那么静态的类初始化的时候会在永久代,而且不会释放,那么创建对象的数量增多的话会导致线程出现这总异常,那么我们在这种情况下需要进行remove操作。如果ThreadLocal里边放入了一些基本类型话不会出现这种情况。

  还有一种情况就是如果线程中的对象需要进行数据共享的话需要将ThreadLocal设置为静态变量。

  好了,针对这块内容的分析和整理就这么多了,如果大家有什么问题的话,请告知~  谢谢。

聊聊ThreadLocal原理以及使用场景-JAVA 8源码的更多相关文章

  1. 【转】Java HashMap 源码解析(好文章)

    ­ .fluid-width-video-wrapper { width: 100%; position: relative; padding: 0; } .fluid-width-video-wra ...

  2. Hadoop之HDFS原理及文件上传下载源码分析(下)

    上篇Hadoop之HDFS原理及文件上传下载源码分析(上)楼主主要介绍了hdfs原理及FileSystem的初始化源码解析, Client如何与NameNode建立RPC通信.本篇将继续介绍hdfs文 ...

  3. 程序兵法:Java String 源码的排序算法(一)

    摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 这是泥瓦匠的第103篇原创 <程序兵法:Java Str ...

  4. Java读源码之ReentrantLock(2)

    前言 本文是 ReentrantLock 源码的第二篇,第一篇主要介绍了公平锁非公平锁正常的加锁解锁流程,虽然表达能力有限不知道有没有讲清楚,本着不太监的原则,本文填补下第一篇中挖的坑. Java读源 ...

  5. java集合源码分析(三):ArrayList

    概述 在前文:java集合源码分析(二):List与AbstractList 和 java集合源码分析(一):Collection 与 AbstractCollection 中,我们大致了解了从 Co ...

  6. java集合源码分析(六):HashMap

    概述 HashMap 是 Map 接口下一个线程不安全的,基于哈希表的实现类.由于他解决哈希冲突的方式是分离链表法,也就是拉链法,因此他的数据结构是数组+链表,在 JDK8 以后,当哈希冲突严重时,H ...

  7. java io 源码研究记录(一)

    Java IO 源码研究: 一.输入流 1  基类 InputStream 简介: 这是Java中所有输入流的基类,它是一个抽象类,下面我们简单来了解一下它的基本方法和抽象方法. 基本方法: publ ...

  8. Java集合源码分析(四)Vector<E>

    Vector<E>简介 Vector也是基于数组实现的,是一个动态数组,其容量能自动增长. Vector是JDK1.0引入了,它的很多实现方法都加入了同步语句,因此是线程安全的(其实也只是 ...

  9. Java集合源码分析(三)LinkedList

    LinkedList简介 LinkedList是基于双向循环链表(从源码中可以很容易看出)实现的,除了可以当做链表来操作外,它还可以当做栈.队列和双端队列来使用. LinkedList同样是非线程安全 ...

随机推荐

  1. Xcode 7.0 官方免费的真机开发

    Xcode 7.0 官方免费的真机开发 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转 ...

  2. OSX: Scripts,让BootCamp在不论什么Mac上支持生成Windows7的USB安装盘

    为什么要做这个呢,由于BootCamp仅仅有在默认的Mac上才会有生成USB安装盘的选项.这个脚本就是为了确保用户使用它在不论什么一个Mac机器上都能够生成Windows的USB安装盘.当然了,假设你 ...

  3. MQTT 简介

    MQTT 全称是 Message Queue Telemetry Transport,是一个轻量级的“发布/订阅”消息传输协议. 官网 http://mqtt.org/ 发布/订阅 MQTT 的基本概 ...

  4. 翻译:Identifier Name标识符命名规则

    */ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hl ...

  5. 三十天学不会TCP,UDP/IP网络编程-UDP,从简单的开始

    如果对和程序员有关的计算机网络知识,和对计算机网络方面的编程有兴趣,欢迎去gitbook(https://www.gitbook.com/@rogerzhu/)star我的这一系列文章,虽然说现在这种 ...

  6. layui动态设置下拉框数据,根据后台数据设置选中

    追加下拉框数据: 设置默认选中: 正常的判断这种情况是不行的,因为追加出的数据,在前台显示的并不是同一个下拉框,原来的下拉框被隐藏了 因此需要:根据原来的位置,寻找下一个节点,寻找子节点的方式找到相应 ...

  7. sqlserver 存储过程 删除

    --删除(delete from) CREATE PROCEDURE [dbo].[DeleteMessage] @strtable varchar(),--要删除信息的表名 @strwhere va ...

  8. OC学习13——Foundation框架中的集合

    OC集合类是一些非常有用的工具类,它可以用于存储多个数量不等的对象,并可以实现常用的数据结构(栈.队列等),此外,OC集合还可用于保存具有映射关系的关联数组.OC的集合大致可以分为:NSArray.N ...

  9. cell的复用机制

    以下全部都是转载自别人的博客:http://blog.sina.com.cn/s/blog_9c3c519b01016aqu.html 转自:http://www.2cto.com/kf/201207 ...

  10. 点击button1弹出form2,并在form2中点击button2来调用form1的方法

    链接地址:http://www.sufeinet.com/thread-1273-1-1.html   1.     private void button1_Click(object sender, ...