理解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虚拟机中对每一个线程都分 ...
随机推荐
- Get familiar with key Frameworks of ios
Frameworks make your life easier as an iOS Developer. They allow you to reuse code written by other ...
- 问题-[Delphi]SendMessageTimeout调用后卡住点击任务栏还会出现窗体处理
问题现象:在使用SendMessageTimeout函数后,5秒后WIN把进程挂在起.这时把程序最小化(原因就是不想让用户看到卡的界面),但点击任务栏按钮界面还原了,拦截消息失败(原因是挂起后消息都放 ...
- Code Forces Gym 100886J Sockets(二分)
J - Sockets Time Limit:1000MS Memory Limit:262144KB 64bit IO Format:%I64d & %I64u Valera ...
- medoo数据库插入的问题
今天遇到疾行客同学提出的medoo insert方法不能插入数据库问题,我测试了一下 发现是可以的 而INSERT INTO "t_user" (username) VALUES ...
- BSEG和BSIS、BSAS、BSID、BSAD、BSIK、BSAK六个表的关系(转)
BSEG和BSIS.BSAS.BSID.BSAD.BSIK.BSAK 六个表的关系 1.数据关系: BSAS+BSIS+BSAK+BSIK+BSAD+BSID = BSEG 2.六个表说明: clea ...
- 有用的javascript外部文件或其他外部文件引用
1.<link href='http://fonts.googleapis.com/css?family=PT+Sans+Narrow' rel='stylesheet' type='text/ ...
- 【Java】通过DES加密和解密工具,对字符串进行加密和解密操作
分享一个非常不错的字符串加密和解密的程序. 可以指定不同的密钥对同一字符串进行不同的加密操作,增强加密性能. Java代码如下: package com.app; import java.securi ...
- dell笔记本通过uefi+gpt模式安装win10系统
安装前,需要确认dell笔记本是否支持uefi 1.使用UltraISO制作硬盘镜像后,过程如下 1) 选择"文件"->"打开",如下 2) 在打开的对话 ...
- CentOS6.5编译安装最新MySQL 5.7.11
安装前工作:1,从官方网址下载MySQL5.7.11源码包,大概49M2,安装好CentOS6.5 64位操作系统.建议update操作系统,以便是此版本最新的3. yum -y install g ...
- linux 下ffmpeg和mencoder安装
ffmpeg和mencoder是进行视频转换和视频抽帧的重要开源工具,支持linux和windows环境下的视频转换和视频抽帧操作.本文章记录在linux这两者工具的安装过程.ffmpeg集成视频编码 ...