源码分析(六)——ThreadLocal(基于JDK1.6)

一、ThreadLocal是什么?

ThreadLocal的概念:线程本地变量,或线程本地存储。

ThreadLocal的作用:它主要是为变量在每个线程中都创建了一个副本,每个线程可以访问自己内部的副本变量。具体是通过它提供的get()和set()方法访问某个变量的线程都有自己的局部变量。

注意以下几点:

 1. 每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。

 2. 将一个公用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦。

3. 线程中的局部变量,是在线程中创建,然后调用ThreadLocal.set()方法设置进去的。

 

 

二、ThreadLocal的定义:

public class ThreadLocal<T> 

 

 

三、ThreadLocal的成员变量:

//ThreadLocal实例hash值,用来区分不同实例

private final int threadLocalHashCode = nextHashCode();

 

//可以看作hash值的一个基值  

private static AtomicInteger nextHashCode = new AtomicInteger();

 

//hash值每次增加量  

private static final int HASH_INCREMENT = 0x61c88647;

 

nextHashCode()方法: 

    /**
     * Returns the next hash code.
     */
    private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }
 它是返回一个hash code,同时对nextHashCode加上增量HASH_INCREMENT。也就是在初始化ThreadLocal的实例过程中,做的仅仅是将一个哈希值赋给实例的threadLocalHashCode,并生成下一个哈希值。

 

 

 

四、ThreadLocal的构造方法:

   //只有一个无参构造函数  

    public ThreadLocal() {     

    }

 

 

五、ThreadLocal的保存线程局部变量方法:

ThreadLocal里还有一个ThreadLocalMap的内部类,它就是用来保存线程局部变量的Map。

set()方法:

    /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

    /**
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

    /**
     * Create the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the map
     * @param map the map to store.
     */
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

 从上面的代码可以看到,set()方法是调用ThreadLocalMap.set()方法保存到ThreadLocalMap实例当中,但是ThreadLocalMap实例却不是保存在Thread中,它通过getMap()方法去取,当取不到的时候就去创建,但是创建之后却是保存在Thread实例中。可以看到Thread.java的代码有如下声明: 

    ThreadLocal.ThreadLocalMap threadLocals = null;

 这种设计很巧妙,线程中有各自的map,而把ThreadLocal实例作为key,这样除了当线程销毁时相关的线程局部变量被销毁之外,还让性能提升了很多。 

 

 

六、ThreadLocal的获取线程局部变量方法:

get()方法:

    /**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }

    /**
     * Variant of set() to establish initialValue. Used instead
     * of set() in case user has overridden the set() method.
     *
     * @return the initial value
     */
    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;
    }

    /**
     * Returns the current thread's "initial value" for this
     * thread-local variable.  This method will be invoked the first
     * time a thread accesses the variable with the {@link #get}
     * method, unless the thread previously invoked the {@link #set}
     * method, in which case the <tt>initialValue</tt> method will not
     * be invoked for the thread.  Normally, this method is invoked at
     * most once per thread, but it may be invoked again in case of
     * subsequent invocations of {@link #remove} followed by {@link #get}.
     *
     * <p>This implementation simply returns <tt>null</tt>; if the
     * programmer desires thread-local variables to have an initial
     * value other than <tt>null</tt>, <tt>ThreadLocal</tt> must be
     * subclassed, and this method overridden.  Typically, an
     * anonymous inner class will be used.
     *
     * @return the initial value for this thread-local
     */
    protected T initialValue() {
        return null;
    }

可以看到在get()方法中,是通过当前线程获取map,再从map当中取得对象。如果取不到(如map为null或取到的值为null),则调用setInitialValue()方法对其设置初始值。setInitialValue()方法会调用initialValue()方法得到一个初始值(默认为null),然后当Thread中的map不为空时,把初始值设置进去,否则为它创建一个ThreadLocalMap对象(但不会调用map的set方法保存这个初始值),最后返回这个初始值。 

 

 

七、ThreadLocal的删除线程局部变量方法:

remove()方法:

    /**
     * Removes the current thread's value for this thread-local
     * variable.  If this thread-local variable is subsequently
     * {@linkplain #get read} by the current thread, its value will be
     * reinitialized by invoking its {@link #initialValue} method,
     * unless its value is {@linkplain #set set} by the current thread
     * in the interim.  This may result in multiple invocations of the
     * <tt>initialValue</tt> method in the current thread.
     *
     * @since 1.5
     */
     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

 

 

 八、ThreadLocal和Synchonized的比较:

1.  相同点:

        ThreadLocal和Synchonized都用于解决多线程并发访问。

2. 不同点:

        ThreadLocal与synchronized有本质的区别:

                2.1.synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。

                而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一

                个对象,这样就隔离了多个线程对数据的数据共享。而synchronized却正好相反,它用于在多个线                 程间通信时能够获得数据共享。

                2.2. synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。

3. ThreadLocal和线程同步机制相比有什么优势呢?

        ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。

        在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。

        而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。

        概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

 

 

 九、示例:

package com.mycom.test.threadlocal;

/**
 * ThreadLocal
 * 
 * @author guweiqiang
 */
public class TestNum {
	
	/**
	 * 通过匿名的内部类覆盖ThreadLocal的initialValue()方法,指定初始值
	 */
	private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>(){
		public Integer initialValue(){
			return 0;
		}
	};
	
	/**
	 * 获取下一个序列值
	 */
	public Integer getNext(){
		seqNum.set(seqNum.get()+1);
		return seqNum.get();
	}
	
	public static void main(String[] args) {
		TestNum tn = new TestNum();
		
		new TestClient(tn).start();
		new TestClient(tn).start();
		new TestClient(tn).start();
	}
	
	private static class TestClient extends Thread{
		private TestNum tn;
		
		public TestClient(TestNum tn){
			this.tn = tn;
		}
		
		public void run(){
			// 每个线程打出4个序列值
			for(int i=0; i<4; i++){
				System.out.println("thread["+Thread.currentThread().getName()+"]-->tn.next="+tn.getNext());
			}
			
		}
	}

}

 

你可能感兴趣的:(threadLocal)