lesson1:threadlocal的使用demo及源码分析
本文中所使用的demo源码地址:https://github.com/mantuliu/javaAdvance 其中的类Lesson1ThreadLocal
本文为java晋级系列的第一讲,后续会陆续推出java相关的高级应用和分析。我个人一直都比较推崇threadlocal的设计原理和实现方式。以下关于threadlocal的描述来源于百度百科:
package com.mantu.advance; /**
* blog http://www.cnblogs.com/mantu/
* github https://github.com/mantuliu/
* @author mantu
*
*/
public class Lesson1ThreadLocal {
public static ThreadLocal<String> local = new ThreadLocal<String>();//声明静态的threadlocal变量
public static ThreadLocal<String> local2 = new ThreadLocal<String>();//声明静态的threadlocal变量
public static void main(String [] args){
for(int i=0;i<5;i++){
TestThread testThread = new TestThread();//创建5个线程
new Thread(testThread).start();
}
} } class TestThread implements Runnable{ @Override
public void run() {
// TODO Auto-generated method stub
try {
Thread.sleep(1l);//让线程停顿一下,便于其它线程执行
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Lesson1ThreadLocal.local.set(Thread.currentThread().getId()+":"+System.currentTimeMillis());
Lesson1ThreadLocal.local2.set(Thread.currentThread().getId()+"");
firstStep();
try {
Thread.sleep(1l);//让线程停顿一下,便于其它线程执行
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
secondStep();
try {
Thread.sleep(1l);//让线程停顿一下,便于其它线程执行
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
thirdStep();
try {
Thread.sleep(1l);//让线程停顿一下,便于其它线程执行
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
fourthStep();
try {
Thread.sleep(1l);//让线程停顿一下,便于其它线程执行
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
fStep();
} public void firstStep(){
System.out.println(Lesson1ThreadLocal.local.get().toString()+":first step");//获取本线程的threadlocal变量值并打印
}
public void secondStep(){
System.out.println(Lesson1ThreadLocal.local.get().toString()+":second step");
}
public void thirdStep(){
System.out.println(Lesson1ThreadLocal.local.get().toString()+":third step");
}
public void fourthStep(){
System.out.println(Lesson1ThreadLocal.local.get().toString()+":fourth step");
}
public void fStep(){
System.out.println(Lesson1ThreadLocal.local.get().toString()+":fifth step");
}
}
代码的主要思路是5个线程,使用了同一个静态的threadlocal变量,每个线程在启动时,存储本线程相关的变量,在后面的5个步骤中都会使用到,展示每个线程的所使用的变量都是独立的,执行结果如下,大家可以自行执行并观察执行结果:
9:1470882533007:first step
11:1470882533023:first step
10:1470882533024:first step
13:1470882533024:first step
9:1470882533007:second step
12:1470882533024:first step
9:1470882533007:third step
12:1470882533024:second step
13:1470882533024:second step
11:1470882533023:second step
10:1470882533024:second step
11:1470882533023:third step
10:1470882533024:third step
12:1470882533024:third step
9:1470882533007:fourth step
13:1470882533024:third step
11:1470882533023:fourth step
10:1470882533024:fourth step
12:1470882533024:fourth step
13:1470882533024:fourth step
9:1470882533007:fifth step
12:1470882533024:fifth step
10:1470882533024:fifth step
13:1470882533024:fifth step
11:1470882533023:fifth step
从执行结果标注红色的部分可以看出,线程id为9的线程在step1到step5的操作过程中,从threadlocal变量中所取到变量值是同一个:9:1470882533007,由此便巧妙的利用了threadlocal变量来实现了在同一个线程内部的变量共享功能,对于同一个变量的操作与其它线程隔离。
下面我们开始分析一下threadlocal的源码,首先从set()方法看起:
public void set(T value) {
Thread t = Thread.currentThread();//获取到当前的线程
ThreadLocalMap map = getMap(t);//通过当前的线程来获取到本线程对应的存储map
if (map != null)
map.set(this, value);//如果map不为空,则在map中存储值value,对应的key为当前的threadlocal对象
else
createMap(t, value);//如果map为空,则创建map,并在map中存储此变量
}
接下来,我们再分析一下createMap()相关的代码
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);//创建ThreadLocalMap,参数为threadlocal变量和之前传递的变量值
}
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);//因为每个线程都有一个独立的map空间,通过线程获取到这个map
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);//通过key值,也就是我们的local变量来获取到实际的变量值
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
从源码中,我们可以发现,每个线程都有一个独立的存储空间,此空间是一个map,map的key值是我们所使用的threadlocal变量,例如文中的ThreadLocal<String> local 变量,此key对应的值为我们存储的变量Thread.currentThread().getId()+":"+System.currentTimeMillis()。通过下面的图可以更好的帮助大家来理解threadlocal变量中线程、存储空间map、threadlocal变量、存储的变量四者间的关系:一个thread有且只有一个存储空间(map),map会对应多个键值对,其中键为threadlocal变量,值为业务使用的实际变量。

lesson1:threadlocal的使用demo及源码分析的更多相关文章
- lesson2:java阻塞队列的demo及源码分析
本文向大家展示了java阻塞队列的使用场景.源码分析及特定场景下的使用方式.java的阻塞队列是jdk1.5之后在并发包中提供的一组队列,主要的使用场景是在需要使用生产者消费者模式时,用户不必再通过多 ...
- caffe web demo运行+源码分析
caffe web demo学习 1.运行 安装好caffe后,进入/opt/caffe/examples/web_demo/的caffe web demo项目目录,查看一下app.py文件,这是一个 ...
- Bytom Dapp 开发笔记(三):Dapp Demo前端源码分析
本章内容会针对比原官方提供的dapp-demo,分析里面的前端源码,分析清楚整个demo的流程,然后针对里面开发过程遇到的坑,添加一下个人的见解还有解决的方案. 储蓄分红合约简述 为了方便理解,这里简 ...
- ThreadLocal 工作原理、部分源码分析
1.大概去哪里看 ThreadLocal 其根本实现方法,是在Thread里面,有一个ThreadLocal.ThreadLocalMap属性 ThreadLocal.ThreadLocalMap t ...
- cocos2D-x demo 的源码分析 #define ..##.. 的妙用.
最近在看cocos2d-x 但不知道如何下手,于是先看一下他编译的完成的testcpp的源码.发现了下面一段程序 typedef CCLayer* (*NEWTESTFUNC)(); #define ...
- ThreadLocal 线程本地变量 及 源码分析
■ ThreadLocal 定义 ThreadLocal通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量 ...
- spring AOP源码分析(一)
对于springAOP的源码分析,我打算分三部分来讲解:1.配置文件的解析,解析为BeanDefination和其他信息然后注册到BeanFactory中:2.为目标对象配置增强行为以及代理对象的生成 ...
- ASimpleCache源码分析
ASimpleCache里只有一个JAVA文件——ACache.java,首先我用思维导图制作了ACache类的详细结构图: 通过分析官方给的demo来驱动源码分析吧 以字符串存储为例(官方给的dem ...
- ThreadLocal详解,ThreadLocal源码分析,ThreadLocal图解
本文脉路: 概念阐释 ----> 原理图解 ------> 源码分析 ------> 思路整理 ----> 其他补充. 一.概念阐述. ThreadLocal 是一个为 ...
随机推荐
- ios 中如何应对UIScrollView快速滑动(暴力用户,暴力测试)
1.实现UIScrollViewDelegate 开始滑动: - (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView 滑动 ...
- Java多线程——ThreadLocal类
一.概述 ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量).也许把它命名 ...
- spring06Aop
1.实现前置增强 必须实现接口MethodBeforeAdvice接口 创建对应的文件 public interface Animal {//主业务接口 void eat(); //目标方法 void ...
- vs连接mysql
1.打开vs2012在aspx中添加一个Grid view 控件,,. 2,选择新建数据源. 3,选择数据库. 4,选择新建连接. 5,更改成mysql连接. 6,这里的Server name 是你自 ...
- 类名.this与类名.class
1..当在内部类中使用this指的就是内部类的对象, 为了访问外层类对象,就可以使用外层类名.this来访问. 2.在java中,每个class都有一个相应的Class对象,当编写好一个类,编译完成后 ...
- 合理使用Memcached进行缓存部署
Memcached是danga.com(运营 LiveJournal的技术团队)开发的一套分布式内存对象缓存系统,用于在动态系统中减少数据库负载,提升性能.关于这个东西,相信很多人都用过,本 文意在通 ...
- MySQL 数据表修复及数据恢复
1. MYSQL数据表在什么情况下容易损坏? 服务器突然断电导致数据文件损坏. 强制关机,没有先关闭mysql 服务等. 2. 数据表损坏后的主要现象是什么? 从表中选择数据之时,得到如下错误:I ...
- C# 导出word文档及批量导出word文档(1)
这里用到了两个dll,一个是aspose.word.dll,另外一个是ICSharpCode.SharpZipLib.dll,ICSharpCode.SharpZipLib.dll是用于批量 ...
- JAVA-3-水仙花
public static void main(String[] args) { // TODO 自动生成的方法存根 int i = 100; while (i < 1000) { int a, ...
- IEnumerable中的 Any方法
IEnumerable类中的 Any方法,表示集合中有任何一元素满足条件,返回就true , 该方法有两个重载 1. 不带任何参数,表示集合中有元素 2. 参入一个 Func<TSource, ...