理解ThreadLocal背后的概念
介绍
我之前在任何场合都没有使用过thread local,因此没有注意到它,直到最近用到它的时候。
前提信息
线程可以理解为一个单独的进程,它有自己的调用栈。在java中每一个线程都有一个调用栈或者说每一个调用栈都有一个线程,即使你不在你的程序中创建线程,线程仍然会在你不知道的情况下运行。最简单的例子就是,当你通过main方法启动一个简单的java程序时,你不在程序里调用new Thread().start(),但是JVM仍然会创建一个main thread 去运行main方法。
main线程有一些特殊,因为所有的其它线程都是在此线程中产生,当此线程运运完成,应用程序的生命周期也就结束了。
在一个web 应用server上一般都会有线程池,因为创建一个线程是一个开销相对比较大的。所有的Java EE(Weblogic,Glassfish,JBoss etc)都有自己的线程池,这就意味着线程池中的线程会根据需要增加或者减少,因此并不会针对每个请求都会创建一个线程,一些已经存在的线程可能会被复用。
理解Thread Local
为了更好的理解Thread local,这里我实现一个最简单的Thread Local.
package ccs.progest.javacodesamples.threadlocal.ex1; import java.util.HashMap;
import java.util.Map; public class CustomThreadLocal { private static Map threadMap = new HashMap(); public static void add(Object object) {
threadMap.put(Thread.currentThread(), object);
} public static void remove(Object object) {
threadMap.remove(Thread.currentThread());
} public static Object get() {
return threadMap.get(Thread.currentThread());
} } 现在你可以在你的应用中任何时候调用CustomThreadLocal的add方法,这个方法所做的事是将当前线程作为key,object作为value添加到map中,从而达到与这个线程相关连。这个object可能是正在执行线程中任何地方你想访问的对象,或者是一个与当前线程保持关连且有许多次重用的复杂对象。
package ccs.progest.javacodesamples.threadlocal.ex1;
public class ThreadContext {
private String userId;
private Long transactionId;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public Long getTransactionId() {
return transactionId;
}
public void setTransactionId(Long transactionId) {
this.transactionId = transactionId;
}
public String toString() {
return "userId:" + userId + ",transactionId:" + transactionId;
}
}
现在是时候使用ThreadContext了。
我将会启动二个线程,在每个线程中都会添加一个ThreadContext实例,这个实例中信息将会传达给每个线程。
package ccs.progest.javacodesamples.threadlocal.ex1;
public class ThreadLocalMainSampleEx1 {
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
ThreadContext threadContext = new ThreadContext();
threadContext.setTransactionId(1l);
threadContext.setUserId("User 1");
CustomThreadLocal.add(threadContext);
//here we call a method where the thread context is not passed as parameter
PrintThreadContextValues.printThreadContextValues();
}
}).start();
new Thread(new Runnable() {
public void run() {
ThreadContext threadContext = new ThreadContext();
threadContext.setTransactionId(2l);
threadContext.setUserId("User 2");
CustomThreadLocal.add(threadContext);
//here we call a method where the thread context is not passed as parameter
PrintThreadContextValues.printThreadContextValues();
}
}).start();
}
}
package ccs.progest.javacodesamples.threadlocal.ex1;
public class PrintThreadContextValues {
public static void printThreadContextValues(){
System.out.println(CustomThreadLocal.get());
}
}
注意:
CustomThreadLocal.add(threadContext)这一行代码将当前线程与ContextThread实例关连起来了。
执行之后结果如下:
userId:User 1,transactionId:1
userId:User 2,transactionId:2
这怎么可能因为我们根本没有传参数给ThreadContext的userId,transactionId到 PrintThreadContextValues。
其实很简单,当调用CustomThreadLocal.get()时,它取的是所关联线程中的对象。
好了,现在让我们看一个真正的ThreadLocal类的例子(上面的CustomThreadLocal仅仅是为了理解ThreadLocal背后的基理,而ThreadLocal以最佳的方式优化了内存,所以非常快)。
package ccs.progest.javacodesamples.threadlocal.ex2;
public class ThreadContext {
private String userId;
private Long transactionId;
private static ThreadLocal threadLocal = new ThreadLocal(){
@Override
protected ThreadContext initialValue() {
return new ThreadContext();
}
};
public static ThreadContext get() {
return threadLocal.get();
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public Long getTransactionId() {
return transactionId;
}
public void setTransactionId(Long transactionId) {
this.transactionId = transactionId;
}
public String toString() {
return "userId:" + userId + ",transactionId:" + transactionId;
}
}
如javadoc所述,ThreadLocal通常用private static 字段修饰。
package ccs.progest.javacodesamples.threadlocal.ex2;
public class ThreadLocalMainSampleEx2 {
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
ThreadContext threadContext = ThreadContext.get();
threadContext.setTransactionId(1l);
threadContext.setUserId("User 1");
//here we call a method where the thread context is not passed as parameter
PrintThreadContextValues.printThreadContextValues();
}
}).start();
new Thread(new Runnable() {
public void run() {
ThreadContext threadContext = ThreadContext.get();
threadContext.setTransactionId(2l);
threadContext.setUserId("User 2");
//here we call a method where the thread context is not passed as parameter
PrintThreadContextValues.printThreadContextValues();
}
}).start();
}
}
package ccs.progest.javacodesamples.threadlocal.ex2;
public class PrintThreadContextValues {
public static void printThreadContextValues(){
System.out.println(ThreadContext.get());
}
}
执行结果如下:
userId:User 1,transactionId:1
userId:User 2,transactionId:2
另一个非常有用的场合是当你有一个非线程安全的复杂对象的时候,一个最普遍的例子我发现是SimpleDateFormat。package ccs.progest.javacodesamples.threadlocal.ex4;
import java.text.SimpleDateFormat;
import java.util.Date; public class ThreadLocalDateFormat {
// SimpleDateFormat is not thread-safe, so each thread will have one
private static final ThreadLocal formatter = new ThreadLocal() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("MM/dd/yyyy");
}
};
public String formatIt(Date date) {
return formatter.get().format(date);
}
} 结论
ThreadLocal有许多用法,这里只列举了二个(我认为是所有用法中的一部分) *真正的每个线程的上下文,如用户ID或事务ID。 *每个线程实例性能。 原文链接:http://java.dzone.com/articles/understanding-concept-behind(部分作了修改)
理解ThreadLocal背后的概念的更多相关文章
- 计算机程序的思维逻辑 (82) - 理解ThreadLocal
本节,我们来探讨一个特殊的概念,线程本地变量,在Java中的实现是类ThreadLocal,它是什么?有什么用?实现原理是什么?让我们接下来逐步探讨. 基本概念和用法 线程本地变量是说,每个线程都有同 ...
- Java编程的逻辑 (82) - 理解ThreadLocal
本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http: ...
- 【转载】计算机程序的思维逻辑 (82) - 理解ThreadLocal
本节,我们来探讨一个特殊的概念,线程本地变量,在Java中的实现是类ThreadLocal,它是什么?有什么用?实现原理是什么?让我们接下来逐步探讨. 基本概念和用法 线程本地变量是说,每个线程都有同 ...
- Effective Objective-C 2.0 — 第二章 对象、消息、运行期 - 第六条:理解“属性”这一概念
开发者通过对象来 存储并传递数据. 在对象之间传递数据并执行任务的过程就叫做“消息传递”. 这两条特性的工作原理? Objective-C运行期环境(Objective-C runtime) ,提供了 ...
- 【Java】深入理解ThreadLocal
一.前言 要理解ThreadLocal,首先必须理解线程安全.线程可以看做是一个具有一定独立功能的处理过程,它是比进程更细度的单位.当程序以单线程运行的时候,我们不需要考虑线程安全.然而当一个进程中包 ...
- 理解maven的核心概念
原文出处:http://www.cnblogs.com/holbrook/archive/2012/12/24/2830519.html 好久没进行java方面的开发了,最近又完成了一个java相关的 ...
- 简单理解ThreadLocal原理和适用场景
https://blog.csdn.net/qq_36632687/article/details/79551828?utm_source=blogkpcl2 参考文章: 正确理解ThreadLoca ...
- 理解 UWP 视图的概念,让 UWP 应用显示多个窗口(多视图)
原文 理解 UWP 视图的概念,让 UWP 应用显示多个窗口(多视图) UWP 应用多是一个窗口完成所有业务的,事实上我也推荐使用这种单一窗口的方式.不过,总有一些特别的情况下我们需要用到不止一个窗口 ...
- 我是如何理解ThreadLocal
ThreadLocal的概念 ThreadLocal从英文的角度看,可以看成thread和local的组合,就是线程本地的意思,我们都知道,看过jvm内存分配的人都知道在jvm虚拟机中对每一个线程都分 ...
随机推荐
- It appears as though you do not have permission to view information for any of the services you requested
- 1 weekend110的NN元数据管理机制 + NN工作机制 + DN工作原理
第一天的笔记,是伪分布hadoop集群搭建, 后面是hadoop Ha的分布式集群搭建 第一天,是HDFS的shell操作 NN工作机制 里面是二进制 DN工作原理 上传完了之后,在hdfs的虚拟路径 ...
- 备忘--简单比较SPSS、RapidMiner、KNIME以及Kettle四款数据分析工具
SPSS.RapidMiner.KNIME以及Kettle四款工具都可以用来进行数据分析,只是彼此有各自的侧重点和有劣势.它们都可以逐步的定义数据分析过程,也同样都可以对数据进行ETL处理.笔者从自己 ...
- UVA10518 - How Many Calls?(矩阵高速幂)
UVA10518 - How Many Calls?(矩阵高速幂) 题目链接 题目大意:给你fibonacci数列怎么求的.然后问你求f(n) = f(n - 1) + f(n - 2)须要多少次调用 ...
- HTTP的报文格式,GET和POST的区别
1.HTTP报文格式 HTTP报文是面向文本的,报文中的每一个字段都是一些ASCII码串,各个字段的长度是不确定的.HTTP有两类报文:请求报文和响应报文. 请求报文: 一个HTTP请求报文由请求行( ...
- 使用dom4j对xml文件进行增删改查
1.使用dom4j技术对dom_demo.xml进行增删改查 首选要下载dom4j的jar包 在官网上找不到,网上搜索了一下在这个链接:http://sourceforge.net/projects/ ...
- epoll使用实例说明
之前一直在讲如何epoll如何好用,但是并没有实例来演示epoll的使用,下面我们就看一个服务器端使用epoll监听大量并发链接的例子. 首先看一下epoll的几个函数的介绍.1.epoll_crea ...
- linux解压缩命令
1.tar -cvf /data/sc2.tar /data (只打包,不压缩) 把/data下的文件打包成 sc.tar 上面两个都是绝对路径噢 tar -zcvf /data/sc2.tar.g ...
- HDU2084JAVA
数塔 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submissi ...
- [学习笔记]设计模式之Adapter
写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 Adapter(适配器)模式主要解决接口不匹配的问题.为此,让我们要回到最初Builder模式创建平行世界时,白雪公主和小霍比特人的谜 ...