聊聊ThreadLocal原理以及使用场景-JAVA 8源码
相信很多人知道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源码的更多相关文章
- 【转】Java HashMap 源码解析(好文章)
.fluid-width-video-wrapper { width: 100%; position: relative; padding: 0; } .fluid-width-video-wra ...
- Hadoop之HDFS原理及文件上传下载源码分析(下)
上篇Hadoop之HDFS原理及文件上传下载源码分析(上)楼主主要介绍了hdfs原理及FileSystem的初始化源码解析, Client如何与NameNode建立RPC通信.本篇将继续介绍hdfs文 ...
- 程序兵法:Java String 源码的排序算法(一)
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 这是泥瓦匠的第103篇原创 <程序兵法:Java Str ...
- Java读源码之ReentrantLock(2)
前言 本文是 ReentrantLock 源码的第二篇,第一篇主要介绍了公平锁非公平锁正常的加锁解锁流程,虽然表达能力有限不知道有没有讲清楚,本着不太监的原则,本文填补下第一篇中挖的坑. Java读源 ...
- java集合源码分析(三):ArrayList
概述 在前文:java集合源码分析(二):List与AbstractList 和 java集合源码分析(一):Collection 与 AbstractCollection 中,我们大致了解了从 Co ...
- java集合源码分析(六):HashMap
概述 HashMap 是 Map 接口下一个线程不安全的,基于哈希表的实现类.由于他解决哈希冲突的方式是分离链表法,也就是拉链法,因此他的数据结构是数组+链表,在 JDK8 以后,当哈希冲突严重时,H ...
- java io 源码研究记录(一)
Java IO 源码研究: 一.输入流 1 基类 InputStream 简介: 这是Java中所有输入流的基类,它是一个抽象类,下面我们简单来了解一下它的基本方法和抽象方法. 基本方法: publ ...
- Java集合源码分析(四)Vector<E>
Vector<E>简介 Vector也是基于数组实现的,是一个动态数组,其容量能自动增长. Vector是JDK1.0引入了,它的很多实现方法都加入了同步语句,因此是线程安全的(其实也只是 ...
- Java集合源码分析(三)LinkedList
LinkedList简介 LinkedList是基于双向循环链表(从源码中可以很容易看出)实现的,除了可以当做链表来操作外,它还可以当做栈.队列和双端队列来使用. LinkedList同样是非线程安全 ...
随机推荐
- Codeforces Round #261 (Div. 2)459D. Pashmak and Parmida's problem(求逆序数对)
题目链接:http://codeforces.com/contest/459/problem/D D. Pashmak and Parmida's problem time limit per tes ...
- Lucene41PostingWriter源代码分析
原来看lucene4.0的posting格式(http://blog.csdn.net/jollyjumper/article/details/30017581),发现这还是比較简单的VInt格式,据 ...
- Android Api 检查參数状态Api
转载请注明出处:http://blog.csdn.net/droyon/article/details/39938677 在进行Android应用程序开发中,android提供了一个非常好的工具类,来 ...
- 在Visual Studio Code中开发Office Add-in
作者:陈希章 发表于 2017年7月13日 上一篇 我介绍了如何在Visual Studio中开发Office Add-in,因为有标准的项目模板,一系列配套的工具,尤其是自带的一键调试功能,可以让开 ...
- Asp.net MVC 填充word并下载
使用Aspose.word填充内容并下载(免费版Aspose) 填充固定模式的Word文档,需要先制作Word模板: Aspose.Words主要通过Words里域(Fields)来控制内容. ...
- SSM学习(一)搭建基础框架
不知不自觉,工作也两年多了,由于公司一直用的是ssh框架,所以所学也一直是ssh.直到有一天,服务器被攻击,tomcat目录下总有莫名其妙的一些文件,这些文件通过远程ftp下载了一些病毒和木马,服务器 ...
- SQL 杂活
例子一:查询两个表数据并且分页展示 select * from ( select ROW_NUMBER() OVER(order by CreateTime desc) as rownum,* fro ...
- 接口json数据与数据库数据循环比对校验
创建测试计划,加载数据库驱动: 线程组: csv配置元件: 注:Filename用的是相对路径,csv文件要与jmeter脚本文件在同一目录 JDBC连接配置: jdbc请求: 用户定义的变量: ht ...
- 【JMeter】获取json响应报文中数组长度
import com.jayway.jsonpath.JsonPath; import com.jayway.jsonpath.Predicate; import net.minidev.json.J ...
- cell上添加倒计时,以及时差问题的解决
原址 http://www.cnblogs.com/zhangmaliang/p/5102518.html 最近项目中用到了tableView的多个cell倒计时系统问题,本觉得很简单的一个事,一做发 ...