`

ThreadLocal 是什么东东

阅读更多
    最早接触ThreadLocal这个东东,还是在学Hibernate的时候,当时看ThreadLocal没明白是干什么的,后来在网上查才明白ThreadLocal的用途,ThreadLocal其实蛮有用的,总结一下具体的原理及用法。
   虽然支持线程局部变量早就是许多线程工具,但 Java Threads API 的最初设计却没有这项有用的功能。而且,最初的实现也相当低效。ThreadLocal 极少受到关注,但对简化线程安全并发程序的开发来说,它却是很方便的。
  ThreadLocal要解决的是什么问题呢?
一个本来应该线程安全的类,里面有一个线程不安全的变量,这样这个类也就线程不安全了,那应该怎么办呢?我们如果能够把这个变量和每个线程绑定,也就是每一个线程拥有这个变量的副本,那么整个对象就成为线程安全的了。一个解决方案就是使用一个Map,key对应于当前的线程,value对应于那个变量,这样我们就可以轻易的获取到当前线程的那个变量的副本了,ThreadLocal就是这个东东。
我们不妨写写大致的代码:
public class ThreadLocal{
 private Map values = Collections.synchronizedMap(new HashMap());
 public Object get(){
  Thread curThread = Thread.currentThread(); 
  Object o = values.get(curThread); 
  if (o == null && !values.containsKey(curThread)){
   o = initialValue();
   values.put(curThread, o); 
  }
  return o; 
 }

 public void set(Object newValue){
  values.put(Thread.currentThread(), newValue);
 }

 public Object initialValue(){
  return null; 
 }
}

当然java的ThreadLocal实现的总体思路也大致如此。
我们看看jdk提供的api文档:
T get()
          返回此线程局部变量的当前线程副本中的值,如果变量没有用于当前线程的值,则先将其初始化为调用 initialValue() 方法返回的值
protected  T initialValue()
          返回此线程局部变量的当前线程的“初始值”。
void remove()
          移除此线程局部变量当前线程的值。
void set(T value)
          将此线程局部变量的当前线程副本中的值设置为指定值。
我们看jdk文档提供了一个例子:
import java.util.concurrent.atomic.AtomicInteger;

 public class UniqueThreadIdGenerator {

     private static final AtomicInteger uniqueId = new AtomicInteger(0);

     private static final ThreadLocal < Integer > uniqueNum = 
         new ThreadLocal < Integer > () {
             @Override protected Integer initialValue() {
                 return uniqueId.getAndIncrement();
         }
     };
 
     public static int getCurrentThreadId() {
         return uniqueId.get();//应该是return uniqueNum.get();
     }
 } // UniqueThreadIdGenerator

这个例子我看的时候没看懂,其实是有错误的 return uniqueId.get();,应该是return uniqueNum.get();我用的是中文翻译过来的jdk api 1.6.0,不知道
大家的jdk帮助文档有没有这个问题.
我们看看Hibernate官方文档提供的一个通过ThreadLocal维护Session的例子:
 public class HibernateUtil {
         private static final SessionFactory sessionFactory;
         static {
                   try {
                            sessionFactory = new Configuration().configure()
                                               .buildSessionFactory();

                   } catch (Throwable ex) {
                            ex.printStackTrace();
                            throw new ExceptionInInitializerError(ex);

                   }

         }
         public static final ThreadLocal tLocalsess = new ThreadLocal();
         // 取得session 
         public static Session currentSession() {
                   Session session = (Session) tLocalsess.get();
                   // 打开一个新的session,如果当前的不可用.
                   try {
                            if (session == null || !session.isOpen()) {
                                     session = openSession();
                                     tLocalsess.set(session);
                            }

                   } catch (HibernateException e) {
                            // 抛出HibernateException异常
                            e.printStackTrace();

                   }
                   return session;
         }
       public static void closeSession() {
                   Session session = (Session) tLocalsess.get();
                   tLocalsess.set(null);
                   try {
                            if (session != null && session.isOpen()) {
                                     session.close();
                            }
                   } catch (HibernateException e) {
                            //抛出 InfrastructureException异常
                   }
         }
//other code
}
这段代码借助threadLocal,每一个线程保存一个session实例,从而避免线程内的频繁创建和销毁session.

其它适合使用 ThreadLocal 但用池却不能成为很好的替代技术的应用程序包括存储或累积每线程上下文信息以备稍后检索之用这样的应用程序。例如,假设您想创建一个用于管理多线程应用程序调试信息的工具。您可以用 DebugLogger 类作为线程局部容器来累积调试信息。在一个工作单元的开头,您清空容器,而当一个错误出现时,您查询该容器以检索这个工作单元迄今为止生成的所有调试信息。
用 ThreadLocal 管理每线程调试日志
public class DebugLogger {
  private static class ThreadLocalList extends ThreadLocal {
    public Object initialValue() {
      return new ArrayList();
    }
    public List getList() { 
      return (List) super.get(); 
    }
  }
  private ThreadLocalList list = new ThreadLocalList();
  private static String[] stringArray = new String[0];
  public void clear() {
    list.getList().clear();
  }
  public void put(String text) {
    list.getList().add(text);
  }
  public String[] get() {
    return list.getList().toArray(stringArray);
  }
}


   在您的代码中,您可以调用 DebugLogger.put() 来保存您的程序正在做什么的信息,而且,稍后如果有必要(例如发生了一个错误),您能够容易地检索与某个特定线程相关的调试信息。 与简单地把所有信息转储到一个日志文件,然后努力找出哪个日志记录来自哪个线程(还要担心线程争用日志纪录对象)相比,这种技术简便得多,也有效得多。
ThreadLocal 在基于 servlet 的应用程序或工作单元是一个整体请求的任何多线程应用程序服务器中也是很有用的,因为在处理请求的整个过程中将要用到单个线程。您可以通过每线程单子技术用 ThreadLocal 变量来存储各种每请求(per-request)上下文信息。
    ThreadLocal 能带来很多好处。它常常是把有状态类描绘成线程安全的,或者封装非线程安全类以使它们能够在多线程环境中安全地使用的最容易的方式。使用 ThreadLocal 使我们可以绕过为实现线程安全而对何时需要同步进行判断的复杂过程,而且因为它不需要任何同步,所以也改善了可伸缩性。除简单之外,用 ThreadLocal 存储每线程单子或每线程上下文信息在归档方面还有一个颇有价值好处:通过使用 ThreadLocal ,存储在 ThreadLocal 中的对象都是 不被线程共享的是清晰的,从而简化了判断一个类是否线程安全的工作。
     当然ThreadLocal并不能替代同步机制,两者面向的问题领域不同。同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信的有效方式;而ThreadLocal是隔离多个线程的数据共享,从根本上就不在多个线程之间共享资源(变量),这样当然不需要对多个线程进行同步了。所以,如果你需要进行多个线程之间进行通信,则使用同步机制;如果需要隔离多个线程之间的共享冲突,可以使用ThreadLocal,这将极大地简化你的程序,使程序更加易读、简洁。
(参考ibm developeworks 不共享有时是最好的)
分享到:
评论
2 楼 supermarshal 2011-06-09  
刚刚也在看文档,这个方法半天没看明白,也觉得有错误诶
public static int getCurrentThreadId() { 
         return uniqueId.get();//应该是return uniqueNum.get(); 
     } 
1 楼 wh8766 2009-08-10  
很详细
多写博主整理~

相关推荐

    ThreadLocal应用示例及理解

    **线程局部变量(ThreadLocal)是Java编程中一个非常重要的工具类,它在多线程环境下提供了线程安全的数据存储。ThreadLocal并不是一个变量,而是一个类,它为每个线程都创建了一个独立的变量副本,使得每个线程都...

    ThreadLocal

    ThreadLocal是Java编程语言中的一个类,用于在多线程环境中提供线程局部变量。它是一种特殊类型的变量,每个线程都有自己的副本,互不影响,从而实现线程间数据隔离。ThreadLocal通常被用来解决线程共享数据时可能...

    ThreadLocal 内存泄露的实例分析1

    在 `LeakingServlet` 的 `doGet` 方法中,如果 `ThreadLocal` 没有设置值,那么会创建一个新的 `MyCounter` 并设置到 `ThreadLocal` 中。关键在于,一旦 `MyCounter` 被设置到 `ThreadLocal`,那么它将与当前线程...

    ThreadLocal整理.docx

    ThreadLocal 整理 ThreadLocal 是 Java 中的一个重要组件,它能够在每个线程中保持独立的副本。这个功能是通过 Thread 类中的 threadLocals 属性来实现的,这个属性实际上是一个 Entry 数组,其中的每个 Entry 都...

    ThreadLocal的几种误区

    ThreadLocal是Java编程中一种非常特殊的变量类型,它主要用于在多线程环境下为每个线程提供独立的变量副本,从而避免了线程间的数据共享和冲突。然而,ThreadLocal在理解和使用过程中容易产生一些误区,这里我们将...

    java事务 - threadlocal

    Java事务和ThreadLocal是两种在Java编程中至关重要的概念,它们分别用于处理多线程环境下的数据一致性问题和提供线程局部变量。 首先,我们来深入理解Java事务。在数据库操作中,事务是一系列操作的集合,这些操作...

    java 简单的ThreadLocal示例

    Java中的ThreadLocal是一个非常重要的工具类,它在多线程编程中扮演着独特角色,尤其在处理线程间数据隔离和共享时。ThreadLocal不是线程本身,而是为每个线程提供一个独立的变量副本,使得每个线程都可以独立地改变...

    正确理解ThreadLocal.pdf

    ### 正确理解ThreadLocal:深入解析其工作原理与应用场景 #### 一、ThreadLocal的基本概念 `ThreadLocal`是Java平台提供的一种线程局部变量的解决方案,它为每一个使用该变量的线程都提供了独立的变量副本,使得每...

    java中ThreadLocal详解

    ### Java中ThreadLocal详解 #### 一、ThreadLocal概述 在Java多线程编程中,`ThreadLocal`是一个非常重要的工具类,它提供了一种在每个线程内部存储线程私有实例的方法。通常情况下,当多个线程共享某个变量时,...

    使用ThreadLocal管理“session”数据

    1. **什么是Session?** Session是HTTP协议中的一个概念,用于存储用户在服务器端的状态信息。当用户登录网站后,服务器会为该用户创建一个session对象,用来保存用户的一些信息,如登录状态、购物车等。通常,这些...

    ThreadLocal原理及在多层架构中的应用

    **线程局部变量(ThreadLocal)是Java编程中一个非常重要的概念,主要用于在多线程环境中为每个线程提供独立的变量副本。ThreadLocal不是一种数据结构,而是一种解决线程间共享数据的方式,它提供了线程安全的局部...

    ThreadLocal简单Demo

    **线程局部变量(ThreadLocal)** 在Java编程中,`ThreadLocal`是一个非常重要的工具类,它用于在多线程环境中提供线程安全的局部变量。`ThreadLocal`并不是一个线程,而是一个线程局部变量的容器,每个线程都有自己...

    设计模式及ThreadLocal资料

    本资料主要聚焦于两种设计模式以及Java中的ThreadLocal特性。 首先,我们来探讨单例模式。单例模式是一种确保一个类只有一个实例,并提供全局访问点的设计模式。在Java中,通常通过私有构造函数、静态工厂方法或...

    ThreadLocal_ThreadLocal源码分析_

    **ThreadLocal概述** ThreadLocal是Java中的一个线程局部变量类,它为每个线程创建了一个独立的变量副本。这意味着每个线程都有自己的ThreadLocal变量,互不干扰,提供了线程安全的数据存储方式。ThreadLocal通常...

    threadLocal

    ThreadLocal是Java编程语言中的一个线程局部变量类,它为每个线程提供了一个独立的变量副本,使得每个线程可以独立地改变自己的副本,而不会影响其他线程所对应的副本。这个特性在多线程环境下处理并发问题时非常...

    ThreadLocal中内存泄漏和数据丢失问题的问题浅析及解决方案.docx

    2. 如果是线程池里的线程用 ThreadLocal 会有什么问题? ThreadLocal 的拓扑图: 虚线代表这弱引用,当前线程保存了 ThreadLocalMap 作为自己的 local 属性,而 Map 中的 key 又弱引用了 ThreadLocal,从而达到了...

    ThreadLocal的用处

    ThreadLocal是Java编程语言中的一个强大工具,它主要用于在多线程环境中为每个线程提供独立的变量副本。这个机制确保了线程之间的数据隔离,避免了共享状态带来的并发问题,提高了程序的安全性和效率。ThreadLocal是...

Global site tag (gtag.js) - Google Analytics