理解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(部分作了修改)
 
 

 
 

  

时间: 2024-10-19 09:05:17

理解ThreadLocal背后的概念的相关文章

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

本节,我们来探讨一个特殊的概念,线程本地变量,在Java中的实现是类ThreadLocal,它是什么?有什么用?实现原理是什么?让我们接下来逐步探讨. 基本概念和用法 线程本地变量是说,每个线程都有同一个变量的独有拷贝,这个概念听上去比较难以理解,我们先直接来看类TheadLocal的用法. ThreadLocal是一个泛型类,接受一个类型参数T,它只有一个空的构造方法,有两个主要的public方法: public T get() public void set(T value) set就是设置

一个例子理解threadLocal用法

ThreadLocal可以使对象达到线程隔离的目的.话不多说直接上代码: /** * 一个例子理解threadLocal * * 一个单例模式的类 */ public class SingleThreadLocalTest { private static SingleThreadLocalTest single = new SingleThreadLocalTest(); private ThreadLocal<String> threadLocal = new ThreadLocal<

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

开发者通过对象来 存储并传递数据. 在对象之间传递数据并执行任务的过程就叫做“消息传递”. 这两条特性的工作原理? Objective-C运行期环境(Objective-C runtime) ,提供了使得对象之间能够传递消息的重要函数,并且包含创建类实例所用的全部逻辑. 第六条:理解“属性”这一概念 property:

理解maven的核心概念

原文链接:http://www.cnblogs.com/holbrook/archive/2012/12/24/2830519.html 好久没进行java方面的开发了,最近又完成了一个java相关的任务,顺便重新体会了 maven 这一利器. 在使用过程中发现以前对maven的理解不够深入,借此机会重新梳理了一下maven的核心概念.相信理解了这些核心概念, 即使长时间不使用,以后再重新上手也会非常容易. 本文以类图的方式,介绍maven核心的12个概念以及相互之间的关系. Table of

深入理解正则表达式环视的概念与用法

在<深入理解正则表达式高级教程-环视>中已经对环视做了简单的介绍,但是,可能还有一些读者比较迷惑,今天特意以专题的形式,深入探讨一下正则表达式的环视的概念与用法. 深入理解正则表达式环视的概念与用法 一.环视的概念 (一)环视概念与匹配过程示例 示例一:简单环视匹配过程 (二)什么是消耗正则的匹配字符? 示例二:一次匹配消耗匹配字符匹配过程 示例三:多次匹配消耗匹配字符匹配过程 二.环视的类型 (一)肯定和否定 (二)顺序和逆序 · 两种类型名称组合 · 四种组合的用法 四种组合正则与环视的摆

【Java】深入理解ThreadLocal

一.前言 要理解ThreadLocal,首先必须理解线程安全.线程可以看做是一个具有一定独立功能的处理过程,它是比进程更细度的单位.当程序以单线程运行的时候,我们不需要考虑线程安全.然而当一个进程中包含多个线程的时候,就需要考虑线程安全问题,因为此时线程可能会同时操作同一个资源,当两个或者两个以上线程同时操作一个资源的时候,就会造成冲突.不一致等问题,即线程不安全. 解决线程安全问题,本质上就是解决资源共享问题,一般有以下手段: 1)可重入(不依赖环境):2)互斥(同一时间段只允许一个线程使用)

理解MySQL——架构与概念

理解MySQL——架构与概念 写在前面:最早接触的MySQL是在三年前,那时候MySQL还是4.x版本,很 多功能都不支持,比如,存储过程,视图,触发器,更别说分布式事务等复杂特性了.但从5.0(2005年10月)开始,MySQL渐渐步入企业级数据库的 行列了:复制.集群.分区.分布式事务,这些企业级的特性,使得现在的MySQL,完全可以应用于企业级应用环境(很多互联网公司都用其作为数据库服务 器,尽管节约成本是一个因素,但是没有强大功能作后盾,则是不可想象的).虽然,MySQL还有很多不足,比

Java 并发:深入理解 ThreadLocal

摘要: ThreadLocal 又名线程局部变量,是 Java 中一种较为特殊的线程绑定机制,用于保证变量在不同线程间的隔离性,以方便每个线程处理自己的状态.进一步地,本文以ThreadLocal类的源码为切入点,深入分析了ThreadLocal类的作用原理,并给出应用场景和一般使用步骤. 一. 对 ThreadLocal 的理解 1). ThreadLocal 概述 ThreadLocal 又名 线程局部变量,是 Java 中一种较为特殊的 线程绑定机制,可以为每一个使用该变量的线程都提供一个

理解ThreadLocal类

1 ThreadLocal是什么 早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路.使用这个工具类可以很简洁地编写出优美的多线程程序. ThreadLocal,顾名思义,它不是一个线程,而是线程的一个本地化对象.当工作于多线程中的对象使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程分配一个独立的变量副本.所以每一个线程都可以独立地改变自己的副本,而不会影响其他线程所对应