介绍

  我之前在任何场合都没有使用过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. Bzoj 2120: 数颜色 && 2453: 维护队列 莫队,分块,bitset

    2120: 数颜色 Time Limit: 6 Sec  Memory Limit: 259 MBSubmit: 2645  Solved: 1039[Submit][Status][Discuss] ...

  2. 借助bool判断使冒泡排序效率提高

    排序问题是编程中最常见的问题.实际应用中,计算机有接近一半时间是在处理有关数据排列的问题,提高排序的效率有助于更快地解决问题. 先来说说平常一般的冒泡算法,使用两个循环,外循环作为整体排序,每趟循环使 ...

  3. fetion for linux

  4. PC-[WIN7]此计算机无法联接家庭组

    不能启用 HomeGroup Listener 解决: 1.设定为自动开始:Function Discovery Provider HostFunction Discovery Resource Pu ...

  5. JavaScript面向对象继承方法

    JavaScript的出现已经将近20多年了,但是对这个预言的褒贬还是众说纷纭.很多人都说JavaScript不能算是面向对象的变成语言.但是JavaScript的类型非常松散,也没有编译器.这样一来 ...

  6. SpringMVC 测试 mockMVC

    SpringMVC测试框架 基于RESTful风格的SpringMVC的测试,我们可以测试完整的Spring MVC流程,即从URL请求到控制器处理,再到视图渲染都可以测试. 一 MockMvcBui ...

  7. 浙大PTA - - 堆中的路径

    题目链接:https://pta.patest.cn/pta/test/1342/exam/4/question/21731 本题即考察最小堆的基本操作: #include "iostrea ...

  8. 修复南尼U盘

    通常拿到手的U盘,用数码之家的ChipGenius一般检测不出主控型号,所以需要用到U盘量产工具来测. 量产工具注意版本 一般靠后的版本才能识别使用, . 如果识别不了==> 自动换成手动,U盘 ...

  9. 色情不是我的所有——在法律边缘起舞的 FC2

    [核心提示] 世界上最大的色情站点之中的一个这项「殊荣」只是是 FC2 无心插柳被用户戴上的一顶帽子,这个从 1999 年成立至今已经超过 15 年历史的互联网活化石,远比你想象中更加高深莫測. 一个 ...

  10. Android开发具体解释之ListView具体解释一

    列表ListView介绍和实例  1.ListView  -- ListActivity -- ListAdapter  2.ArrayAdapter结合ListView进行显示  3.SimpleA ...