理解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虚拟机中对每一个线程都分 ...
随机推荐
- Implement the hash table using array / binary search tree
今天在复习Arrays and String 时看到一个很有趣的问题.希望跟大家分享一下. Implement the hash table using array / binary search t ...
- phonegap与google analytics整合
用phonegap开发的app接近尾声,需要整一个谷歌分析进去. 1.首先申请一个GA帐号,在“what would you like to track”下选择APP
- Mitmproxy首页、文档和下载 - 支持SSL的HTTP代理 - 开源中国社区
Mitmproxy首页.文档和下载 - 支持SSL的HTTP代理 - 开源中国社区 undefined 利用Dnspod api批量更新添加DNS解析[python脚本] - 推酷 undefined
- MySQL数据库MyISAM和InnoDB存储引擎的比较(转)
MySQL有多种存储引擎,MyISAM和InnoDB是其中常用的两种.这里介绍关于这两种引擎的一些基本概念(非深入介绍). MyISAM是MySQL的默认存储引擎,基于传统的ISAM类型,支持全文搜索 ...
- 使用wampserver安装Composer的注意事项
http://getcomposer.org/Composer-Setup.exe 修改C:\wamp\bin\php\php5.3.10中php.ini中的配置 在php.ini中开启php_ope ...
- CSS围住浮动元素的三种方法
浮动元素脱离了文档流,其父元素看不到它了,因而不会包围它.浮动会“扩散”到下一个清除浮动的元素处.这会引起不想要的页面布局效果. 清除浮动的方法有三种: 1.父元素overflow:hidden 2. ...
- ubuntu 安装软件(apt源)
最近使用docker 构建python3的环境: 进入容器发现 连个vi命令多没有... 1.安装一个呗: apt-get 报错:root@22f41d59e3b2:~# apt-get instal ...
- iphone自定义铃声
Step1:下载iTunes Step2:连接手机登录iTunes并授权将音乐文件添加到资料库,修改音乐时间长度为40s Step3:在主界面选择音乐标签 Step4:选择一个mp3音乐文件,点击文件 ...
- Java_Web 连接池
对于共享资源,有一个很著名的设计模式:资源池(Resource Pool).该模式正是为了解决资源的频繁分配﹑释放所造成的问题.为解决我们的问题,可以采用数据库连接池技术.数据库连接池的基本思想就是为 ...
- 【C#】与C及OC的不同点
事实上熟悉这些语言的朋友们深知,这几个语言全然没有可比性. 因为工作须要,近期须要重温C#语言,难免会受到C和OC的基础知识影响. 此篇是本人的一个学习笔记.仅此献给有C/OC基础,须要继续学习C# ...