理解 ThreadLocal

理解 ThreadLocal

      ThreadLocal是线程变量的意思,不是本地线程。它可以为线程分配特有的空间(与线程自身的堆栈不同,是线程对象在内存中的一个Map),使得线程可以通过这个空间随时获取自己相关的上下文信息。

ThreadLocal 内幕

ThreadLocal很容易让使用者误解(以前我就误解了),因为在这个类中定义了一个静态的包级可见的内部类ThreadLocalMap,同时ThreadLocal对外提供了get和set方法。那么set的时候就是把当前线程对象作为ThreadLocalMap的key,把传入set方法的值作为value存储起来;get的时候通过获取当前线程对象,然后到ThreadLocalMap中取得对应的value。这种认识是表面的很容易理解的方式,但是也是错误的理解。

每个Thread类中都有ThreadLocal.ThreadLocalMap的两个成员,分别是threadlocals与inheritableThreadLocals。前者用来存储当前线程的相关数据信息,后者用来存储从父线程中继承或者说拷贝过来的相关数据信息。这两个区域也就是前面提到的线程的特别存储区域,但是如何操作它们?JDK中的设计并不是让用户直接操作当前线程来存取数据,而是让用户操作ThreadLocal来对当前线程的这两个Map进行存取(个人认为这样设计是出于安全性与易用性考虑,主要是Map中的key值需要固定才能取到同一对象),同时放置在这两个Map中的key值就是ThreadLocal对象自身,value由用户传入。


ThreadLocal 进阶

ThreadLocal为线程独占某些对象或者资源提供了新的手段,ThreadLocal与线程堆栈都是用来存取线程私有数据的区域,别的线程无法触碰这两个区域,但是二者确有不同的生命周期。这个可以这样理解,线程跨多个方法调用的时候,方法作为Frame不断的进栈和出栈(方法的调用与返回),同时出栈的时候方法中的临时变量数据会被回收,在这个线程的堆栈中消失,所以方法中的局部变量是无法在线程调用过程中穿透,除非是传递局部的引用类型变量,但是这也意味着方法接口都要额外接收新的参数(早期系统中传递JDBC数据库连接的方式)。而ThreadLocal不同,准确地说是线程中的ThreadLocalMap不同,线程对象中的ThreadLocalMap对象是伴随整个线程对象生命周期的,线程可以在任何需要的时候通过它来获取相关数据,这为线程独占数据提供了一种更加优雅的方式。只不过这一切都是通过ThreadLocal来操作的而已,同时ThreadLocal自身也作为不同线程中的ThreadLocalMap中的key来映射存入的value。

ThreadLocal 应用

public class ThreadWriter

{

   private static final ThreadLocal<FileWriter> local = new

ThreadLocal<FileWriter>();

    public void write(String s)

    {

       try

       {

           getFileWriter().write(s);

       }

       catch(IOException e)

       {

           //igore

       }

    }

    private FileWriter getFileWriter()

    {

       FileWriter fw = local.get();

       if (null == fw)

       {

           try

           {

fw = new FileWriter(Thread.currentThread().getName() + "-info.txt", true);

              local.set(fw);

           }

           catch(IOException e)

           {

             //igore

           }

       }

       return fw;

    }

}

这里说明几个问题:

1、 ThreadLocal对象只有一份,它将作为不同线程中的ThreadLocalMap对象的key,所映射的值是不同文件路径的FileWriter对象。只不过不同 线程中的ThreadLocalMap中的key值都一样,但是因为在不同的ThreadLocalMap中,不会有任何影响,反而这样的方式为每个线程中ThreadLocalMap对象中的key值生成省去了很多功夫,这是一个很精妙的设计,用户在间接使用ThreadLocalMap的时候,不需要感知key是什么,就可以方便地存取。

2、 一份ThreadLocal对象只能映射一种资源。如需要映射多种资源的话,定义多个ThreadLocal成员。

3、 local.set(fw); 这句代码表示将local自身作为key,fw作为value,存储在调用这行代码的线程中的ThreadLocalMap对象中。

FileWriter fw = local.get(); 这句代码表示将local自身作为key,从当前线程的ThreadLocalMap对象中取出属于自己独占的FileWriter对象。 

你可能感兴趣的:(理解 ThreadLocal)