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类的源码 ...
随机推荐
- java调用exe,及调用bat不成功的解决办法
开门见山的说,文件目录如下 想调用123.exe,但是尝试了几次调用不到,然后写了个bat.初始内容如下 @echo off D: cd test "123.exe" 双击可以运行 ...
- 深度学习变革视觉计算总结(CCF-GAIR)
孙剑博士分享的是<深度学习变革视觉计算>,分别从视觉智能.计算机摄影学和AI计算三个方面去介绍. 他首先回顾了深度学习发展历史,深度学习发展到今天并不容易,过程中遇到了两个主要障碍: 第一 ...
- Python接口测试-利用登录后的session用到登录后的接口中
有些接口是在登录后才能调用的,例如“立即出借”只有在登录后才能到出借窗口,解决: 主要是添加了: s =requests.session() 完整代码: '''登录 ''' print('*'*100 ...
- mybatis之动态SQL操作之删除
/** * 持久层 */ public class StudentDao { /** * 动态SQL--删除 */ public void dynaSQLwithDelete(int... ids) ...
- Linux下四款常见远程工具比较
摘要:Linux远程可不像Windows下那么方便,主要是连接的速度.显示的画质不能令人满意(延迟.撕裂).本文只是说一下我用过的四款远程工具.Anydesk官网:https://anydesk.co ...
- lumen中间件中设置响应header
<?php namespace App\Http\Middleware; use Closure; class BeforeMiddleware { public function handle ...
- Python multiprocess模块(中)
主要内容: 一. 锁 二. 信号量 三. 事件 通过event来完成红绿灯模型 四. 队列(重点) 队列实现进程间的通信 五. 生产者消费者模型 1. 初始版本(程序会阻塞住) 2. 升级版本一(通过 ...
- vs .net WebForm 模板添加注释
首先说一点,好久不更新博客了.今天公司服务器不能用了,闲着没事儿,更新一篇博客. 今天要说的就是vs2010下,如何更改各种文件的模板,以省去每次新添加文件时,还得给文件添加文件标示注释等时间.很大程 ...
- co源码
co模块整体包括三部分 对于几种参数类型的判断,主要判断是否object,array,promise,generator,generatorFunction这几种; 将几种不同的参数类型转换为prom ...
- day29 元类及异常处理
元类及异常处理 元类 什么是元类 在python中,一切皆对象,对象是由类产生的,那么类是不是对象呢? 举例: class A: pass print(type(A)) # <class 'ty ...