ThreadLocal的原理与使用
前言
在java web项目中,经常会使用到单例对象,从服务器启动那一时刻就实例化全局对象。然后会对某些全局对象的属性进行修改之类的操作,但是我们知道项目一般都是部署到tomcat、Jboss之类的服务器上。浏览器的每个请求就是一个新的线程,这样如果 对全局对象的属性进行修改并使用,很可能就会造成数据不一致的错误问题。那怎么保证各自线程能正确使用自己修改过的共享变量呢?这时让我们想到ThreadLocal,那ThreadLocal是什么,为何能有如此神奇的行为呢?带着这个问题我们直接进入主题。
什么是ThreadLocal?
ThreadLocal是java.lang包下面的一个类。见名知意,局部的线程。它能避免发生多线程对共享变量修改造成的数据错误问题!
ThreadLocal的底层原理
ThreadLocal能使变量值和线程对象关联起来,保证线程封闭。下面是ThreadLocal类主要的方法:
public T get();
public void set(T value);
public void remove();
private T setInitialValue();
get方法是获取保存在ThreadLocal中当前线程设置的共享变量副本值;
set方法是用来设置当前线程的共享变量副本;
set方法
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
set方法会获取当前线程(线程中有一个属性threadLocals,该属性属于ThreadLocal.ThreadLocalMap类)。如果当前线程的ThreadLocalMap对象不为空,直接把修改的值存放到ThreadLocalMap中;如果为空,则先实例化ThreadLocalMap对象,再存值。
ThreadLocalMap和HashMap相似,也是通过哈希表的数据结构来保存数据(数组加链表)。从set方法可以知道,一个线程有且只会创建一个ThreadLocalMap对象,线程会把修改后的变量值(变量副本)保存到ThreadLocalMap中。
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
} 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);
}
get方法
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
get方法会获取到当前线程ThreadLocalMap对象中保存的副本值。
注意:同一个ThreadLocal对象下,保存在ThreadLocalMap中的Entry对象下标相同。因为下标计算=threadLocalHashCode & (len-1); threadLocalHashCode 和len都是相同的。
remove方法
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
获取当前线程的ThreadLocalMap对象,对象不为空,则调用remove(this);看看这个方法做了什么。
private void remove(ThreadLocal key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
if (e.get() == key) {
e.clear();
expungeStaleEntry(i); // 清空Entry[] 数组对应下标的对象
return;
}
}
}
remove的目的是帮助GC清除多余对象,避免造成内存溢出。
实际项目中的运用
项目中有一个全局对象HttpJsonResource,服务启动的时候初始化。初始化后会维护一个HttpJsonService对象到属性中,通过get方法能获取到HttpJsonService对象。看代码分析:
HttpJsonService service = getHttpJsonService(context, JYLS_URL);
String originUrl = service.getHttpURL();
// HttpJsonService 中的httpURL是个全局变量,originUrl是公用的套接字 String newUrl = originUrl + this.urlSuffix; // urlSuffix是不同的服务编码
.....
使用HttpURLConnection调用其它服务;
HttpURLConnection conn;
URL url = new URL(originUrl + threadLocalURL.get());
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod(super.getReqMethod().toUpperCase());
conn.setRequestProperty("Connection", "close");
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/json;charset=utf-8");//设置参数类型是json格式
// 启动了一个线程
HttpCommProcessThread task = new HttpCommProcessThread(this, conn, reqData, super.getReqMethod());
task.startUp();
// 阻塞主线程
task.waitForData(timeOut);
.
.
// 最后把HttpURL设置为原始的公用值
service.setHttpURL(originUrl);
代码逻辑主要是获取到一个公用的http请求地址httpURL,然后拼接上服务编码,组成一个新的http请求去调用外部服务,调用完之后又把httpURL修改为原始值。看上去是OK的!但是有一个问题,调用接口比如超时了,在这一段时间内,A请求线程还被阻塞,又有一个请求B来调用,这时上A请求没来得及把httpURL修改为原始值,B请求又在A请求修改为新的url基础上进行拼接,导致utl错误。
ThreadLocal的原理与使用的更多相关文章
- 线程局部变量ThreadLocal的原理及使用范围_1
线程局部变量ThreadLocal的原理及使用范围 使用原理 每个Thread中都有一个ThreadLocalMap成员, 该成员是ThreadLocal的内部类ThreadLocalMap类型.每使 ...
- ThreadLocal的原理和在框架中的应用
ThreadLocal的原理和在框架中的应用 博客分类: java基础 框架多线程SpringthreadDAO 概述 我们知道Spring通过各种DAO模板类降低了开发者使用各种数据持久 ...
- ThreadLocal的原理及产生的问题
点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人. 文章不定期同步公众号,还有各种一线大厂面试原题.我的学习系列笔记. ThreadLocal的原理 特点 ThreadLocal和Sychro ...
- ThreadLocal 工作原理、部分源码分析
1.大概去哪里看 ThreadLocal 其根本实现方法,是在Thread里面,有一个ThreadLocal.ThreadLocalMap属性 ThreadLocal.ThreadLocalMap t ...
- ThreadLocal工作原理
原文出处: imzoer 在这篇文章中,总结了一下面试过程中遇到的关于ThreadLocal的内容.总体上说,这样回答,面试算是过得去了.但是,这样的回答,明显仅仅是背会了答案,而没有去研究Threa ...
- ThreadLocal的原理,源码深度分析及使用
文章简介 ThreadLocal应该都比较熟悉,这篇文章会基于ThreadLocal的应用以及实现原理做一个全面的分析 内容导航 什么是ThreadLocal ThreadLocal的使用 分析Thr ...
- 对ThreadLocal实现原理的一点思考
前言 在<透彻理解Spring事务设计思想之手写实现>中,已经向大家揭示了Spring就是利用ThreadLocal来实现一个线程中的Connection是同一个,从而保证了事务.本篇博客 ...
- 【原理】Java的ThreadLocal实现原理浅读
当前线程的值传递,ThreadLocal 通过ThreadLocal设值,在线程内可获取,即时获取值时在其它Class或其它Method. public class BasicUsage { priv ...
- ThreadLocal实现原理
一.ThreadLocal介绍 这是一个线程的局部变量.也就是说,只有当前线程可以访问.既然是只有当前线程可以访问的数据,自然是线程安全的. 为每一个线程分配不同的对象,需要在应用 ...
- ThreadLocal使用原理、注意问题、使用场景
想必很多朋友对ThreadLocal并不陌生,今天我们就来一起探讨下ThreadLocal的使用方法和实现原理.首先,本文先谈一下对ThreadLocal的理解,然后根据ThreadLocal类的源码 ...
随机推荐
- 2.JSON.stringify()Object
JSON.stringify() JSON 通常用于与服务端交换数据. 在向服务器发送数据时一般是字符串. 我们可以使用 JSON.stringify() 方法将 JavaScript 对象转换为字符 ...
- 微信一键登录(微信OAuth2.0)
1.注册微信开放平台https://open.weixin.qq.com,一定要清楚微信开放平台和微信公众平台是分别独立的,不能共用. 2.登录进入——管理中心,网站应用,创建网站应用.填写申请,企业 ...
- Fragment向下兼容
* android-support-v4都用这个包里的类* 让activity继承FragmentActivity* 获取管理器 getSupportFragmentManager();
- itchat库微信自动回复祝福语
过年了,之前看到一些python文章介绍用itchat自动回复微信,我自己就写了一个. 官方文档https://itchat.readthedocs.io/zh/latest/,这个库挺简洁的,对着接 ...
- 等待数据库引擎恢复句柄失败 SqlServer2012安装时报错 Win10
上周,在一批Win10系统电脑上安装SqlServer 2012时,屡次发生报错,安装失败,显示的失败信息是:等待数据库引擎恢复句柄失败 如下图所示: 面对这样的错误,我的第一反应是百度,在百度上找了 ...
- 1. Linux基本操作和基本命令
常用快捷键: Ctrl + d : 结束符 Ctrl + c : 中断前台进程 Ctrl + z : 将前台进程停止掉 创建终端: 创建终端标签:Ctrl + Shift + t; 切换标签: A ...
- 在java中有关于反射的皮毛----自己的简略认知
白首为功名.旧山松竹老,阻归程.欲将心事付瑶琴.知音少,弦断有谁听? 反射(reflection): 当我们在看到这个名词首先会想到的是,我们在上高中时学的物理,那么在java开发中,反射这个名词是怎 ...
- bert系列二:《BERT》论文解读
论文<BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding> 以下陆续介绍ber ...
- 关于python脚本头部设置#!/usr/bin/python
今天又是贼几把菜的一天0.0 读别人程序的时候看到在python文件头部设置签名,感觉贼几把酷,自己也试着在文件前段设置了一下. 设置还是蛮简单的,设置过程如图所示. 设置后如图所示: 当然你也可能看 ...
- Elasticsearch基础入门,详情可见官方文档
索引文档: 对于员工目录,我们将做如下操作: 每个员工索引一个文档,文档包含该员工的所有信息. 每个文档都将是 employee 类型 . 该类型位于 索引 megacorp 内. 该索引保存在我们的 ...