ThreadLocal 源码分析

    之前曾转载过相关ThreadLocal的文章,但一直是处于迷糊状态,最近复习的时候偶然看到博客里的文章,所以对此类做一个深入的分析和总结。

(另外说明下,我这个源码的Java 版本是1.6.0_06)

 

    在Java API 文档里,是这样解释的:

该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。

    这里有两个关键字,“线程局部变量”和“初始化副本”。这两个关键字基本涵盖了这个类的含义。

   “线程局部变量”是指在同一个线程中,该变量是唯一的(可以把这个变量理解为同个线程内的单例)。

   “初始化副本”是指在不同线程中,该变量是你定义的那个初始值(也就是那个副本)。

 

   下面是Java API 的例子:

    以下类生成对每个线程唯一的局部标识符(就是说在同一个线程内是不会出现两个相同的uniqueId的)。线程 ID 是在第一次调用 UniqueThreadIdGenerator.getCurrentThreadId() 时分配的,在后续调用中不会更改。

 

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() {
//API 上的例子是下面的,不过我以为应该是写错了,因为这样的话每次获得的uniqueId是不变的,都是0
     //    return uniqueId.get();
//我修改后的代码如下,就是把uniqueId换成uniqueNum 
           return uniqueNum.get();
     }
 } // UniqueThreadIdGenerator

 

   下面是对ThreadLocal 类的源码分析下:

   void set(Object value):设置当前线程的线程局部变量的值。
   public Object get() :该方法返回当前线程所对应的线程局部变量。 (同一个线程获取同一个变量的引用)
   public void remove() :将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。

   protected Object initialValue():返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的(在上面的例子也可以看到,其实就是一个默认值的设置)。这个方法是一个延迟调用方法,在线程第1次调用 get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。

 

   1、void set(Object value)方法分析

public void set(T value) {
        Thread t = Thread.currentThread();
//查看当前线程中的ThreadLocalMap 是否存在。
        ThreadLocalMap map = getMap(t);
//存在的话就把当前线程设置进ThreadLocalMap中。
        if (map != null)
            map.set(this, value);
        else
//否则生成一个ThreadLocalMap 对象,并把值赋到当前线程的ThreadLocalMap中
            createMap(t, value);
    }
//-------------------------------解析的代码分割线-------------------------------

//返回当前线程的ThreadLocalMap 
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
//创建新的ThreadLocalMap赋值到当前线程
 void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

 

 

 

 

    下面说下关于ThreadLocalMap 类,这个类有点类似于WeakHashMap(这个类对于解决某些问题提供了很好的性能上的方案,有空再继续深入了解。),虽然ThreadLocalMap没有继承Map的,但它具有Map的一些特性,有关这些特性就不说了,主要说说它们的一个很大的共同点就是,它们的key 都是弱引用的对象,因此在该键没有被引用时,将会被JVM收集。(有关弱引用的解析,请参考我的博客上的:http://zhxing.iteye.com/admin/blogs/571007

ThreadLocalMap是ThreadLocal的一个嵌套类,它的一些源码如下:

    static class ThreadLocalMap {

        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
        }

 

2、 public Object get()  方法分析

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
        //这个是传入当前的ThreadLocal 对象引用获取所对应的值
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
  //如果map 对象为空,说明是第一次调用这个方法,之前也没有调用set()方法
        return setInitialValue();
    }
//-------------------------------解析的代码分割线-------------------------------
//这个方法很简单就是调用initialValue()获取初始值(默认值),而且要赋值在当前线程的map中(相当于调用了set()方法)
    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
//ThreadLocal 的默认实现是返回null 的,所以如果在多个线程中使用时,如果需要用到默认值,那子类就应该覆盖该值
    protected T initialValue() {
        return null;
    }

 

 

 

3、public void remove() 方法

//这个方法没什么可说的,就是删除掉当前线程中的变量(释放内存)
public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

 

总结

 

 

   ThreadLocal 可以实现在同一个线程内共享一个变量,不同的线程不能共享,只能得到该变量的一个副本(就是一个默认的初始化值)。

     在论坛中也有人提到ThreadLocal和synchronized 的问题,我觉得它们都是解决线程的两个不同的方案。ThreadLocal解决的是同一个线程内的资源共享问题,而synchronized 解决的是多个线程间的资源共享问题。

有关的讨论在:http://www.iteye.com/topic/179040

 

有关这个类的应用,可以参考http://zhxing.iteye.com/admin/blogs/306070

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值