介绍

  我之前在任何场合都没有使用过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背后的概念的更多相关文章

  1. 计算机程序的思维逻辑 (82) - 理解ThreadLocal

    本节,我们来探讨一个特殊的概念,线程本地变量,在Java中的实现是类ThreadLocal,它是什么?有什么用?实现原理是什么?让我们接下来逐步探讨. 基本概念和用法 线程本地变量是说,每个线程都有同 ...

  2. Java编程的逻辑 (82) - 理解ThreadLocal

    ​本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http: ...

  3. 【转载】计算机程序的思维逻辑 (82) - 理解ThreadLocal

    本节,我们来探讨一个特殊的概念,线程本地变量,在Java中的实现是类ThreadLocal,它是什么?有什么用?实现原理是什么?让我们接下来逐步探讨. 基本概念和用法 线程本地变量是说,每个线程都有同 ...

  4. Effective Objective-C 2.0 — 第二章 对象、消息、运行期 - 第六条:理解“属性”这一概念

    开发者通过对象来 存储并传递数据. 在对象之间传递数据并执行任务的过程就叫做“消息传递”. 这两条特性的工作原理? Objective-C运行期环境(Objective-C runtime) ,提供了 ...

  5. 【Java】深入理解ThreadLocal

    一.前言 要理解ThreadLocal,首先必须理解线程安全.线程可以看做是一个具有一定独立功能的处理过程,它是比进程更细度的单位.当程序以单线程运行的时候,我们不需要考虑线程安全.然而当一个进程中包 ...

  6. 理解maven的核心概念

    原文出处:http://www.cnblogs.com/holbrook/archive/2012/12/24/2830519.html 好久没进行java方面的开发了,最近又完成了一个java相关的 ...

  7. 简单理解ThreadLocal原理和适用场景

    https://blog.csdn.net/qq_36632687/article/details/79551828?utm_source=blogkpcl2 参考文章: 正确理解ThreadLoca ...

  8. 理解 UWP 视图的概念,让 UWP 应用显示多个窗口(多视图)

    原文 理解 UWP 视图的概念,让 UWP 应用显示多个窗口(多视图) UWP 应用多是一个窗口完成所有业务的,事实上我也推荐使用这种单一窗口的方式.不过,总有一些特别的情况下我们需要用到不止一个窗口 ...

  9. 我是如何理解ThreadLocal

    ThreadLocal的概念 ThreadLocal从英文的角度看,可以看成thread和local的组合,就是线程本地的意思,我们都知道,看过jvm内存分配的人都知道在jvm虚拟机中对每一个线程都分 ...

随机推荐

  1. Python自动化之session

    request.body 所有请求内容的原生数据 request.META 所有请求头的原生数据 cookie返回存在于响应头里面 session session是保存在服务端的键值对 cookie和 ...

  2. jquery-data的三种用法

    1.jquery-data的用处 jQuery-data主要是用来存储数据,帮助普通对象或者jQuery对象来存储数据,其实如果单纯的储存dom的单一的属性,用attr自定义属性足够了:如果存储多个键 ...

  3. wpf DataGrid CheckBox列全选

    最近在wpf项目中遇到当DataGrid的header中的checkbox选中,让该列的checkbox全选问题,为了不让程序员写自己的一堆事件,现写了一个自己的自定义控件 在DataGrid的 &l ...

  4. 怎样在win7下装ubuntu(硬盘版安装)

    1)首先还是分区,在计算机上右键--管理--磁盘管理 装Ubuntu分配的硬盘大小最好是(20G以上)不要太小,这里请注意,ubuntu和windows文件系统全然不同,所以我们划好要给ubuntu的 ...

  5. Android(java)学习笔记196:Android中Menu的使用(静态和动态)

    1.使用xml定义Menu(静态方法) 菜单资源文件必须放在res/menu目录中.菜单资源文件必须使用<menu>标签作为根节点.除了<menu>标签外,还有另外两个标签用于 ...

  6. dll注册到GAC还是bin - sharepoint程序

    通常来说程序在使用dll的时候,会先去GAC中查找是否有存在合适的dll,然后才会到应用程序下的bin目录去查找: 前几天遇到了一个奇葩问题,web项目工程添加了一个第三方dll的引用,然后把这个第三 ...

  7. jstl中添加自定义的函数

    由于jstl中提供的函数未必能够满足我们的要求,而我们又希望能够像jstl提供的函数那样能够轻松方便使用,那么可以通过自定义函数补充jsltl函数.给jstl添加自定义函数需要以下步骤: 定义一个st ...

  8. HTML5 Canvas 2D绘图

    为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/ShiJiaqi. http://www.cnblogs.com/shijiaqi1066/p/4851774. ...

  9. jQuery滑动并响应事件

    jQuery滑动并打开指定页面: <!DOCTYPE html> <html> <head> <script src="http://code.jq ...

  10. MVC Ajax 提交是防止SCRF攻击

    //在View中 <script type="text/javascript"> @functions{ public string ToKenHeaderValue( ...