`

源码分析(六)——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());
			}
			
		}
	}

}

 

分享到:
评论

相关推荐

    jdk1.6中文API

    **Java Development Kit (JDK) 1.6 中文API** JDK 1.6,也被称为Java SE 6,是Java平台标准版的一个重要版本,由Sun Microsystems(后被Oracle收购)发布。这个版本引入了许多增强功能和新特性,旨在提高开发效率、...

    jdk1.6帮助文档

    4. **并发与多线程**:JDK 1.6对并发库进行了大量优化,包括`java.util.concurrent`包中的工具类,如`ExecutorService`、`Future`、`Semaphore`等,以及`java.lang.ThreadLocal`的增强。 5. **NIO.2**:Java New I/...

    最实用的JDK1.6API

    《最实用的JDK1.6 API》是一个针对Java开发者的重要资源,它包含了JDK1.6版本的中文API文档,对于理解并高效利用Java语言进行软件开发具有极高的价值。JDK(Java Development Kit)是Java编程环境的核心组件,其中的...

    java JDK1.6 CHM中文参考

    该CHM文件包含了Java 1.6版本的关键内容,虽然描述中提到了"基于1.7api的函数说明",但实际上,它是关于JDK1.6的,因此应关注的是JDK1.6中的核心类库、接口和方法。在Java 1.6中,开发者可以找到诸如集合框架、IO流...

    Jdk+api+1.6+英文原版 CHM格式

    《Jdk+api+1.6+英文原版 CHM格式》是针对Java开发者的一份重要参考资料,它包含了Java Development Kit (JDK) 1.6版本的API文档,以CHM(Compiled Help Manual)格式呈现。CHM是一种微软开发的帮助文件格式,允许...

    ThreadLocal_ThreadLocal源码分析_

    在JDK 8之前,这个内部Map是`ThreadLocal.ThreadLocalMap`,而从JDK 8开始,改为了`WeakReference&lt;ThreadLocal&lt;?&gt;&gt;`和`Object`的组合,提高了内存管理效率。 **JDK 8之前的ThreadLocalMap** 在JDK 8之前,...

    java并发包源码分析(3)ThreadLocal

    Java并发编程中,ThreadLocal是一个非常重要的类,它是解决线程安全问题的一个工具。ThreadLocal提供了一种在多线程环境下使用“共享变量”的方式,但是它实际上是为每个线程提供一个变量的副本,这样每个线程都可以...

    ThreadLocal源码分析

    首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。各个线程中访问的是不同的对象。

    ThreadLocal源码分析和使用

    ThreadLocal 源码分析和使用 ThreadLocal 是 Java 语言中的一种多线程编程机制,用于解决多线程程序的并发问题。它不是一个 Thread,而是一个 Thread 的局部变量。ThreadLocal 的出现是为了解决多线程程序中的共享...

    JDK的ThreadLocal理解(一)使用和测试

    **标题:“JDK的ThreadLocal理解(一)使用和测试”** **正文:** ThreadLocal是Java中的一个非常重要的线程安全工具类,它在多线程编程中扮演着独特的角色。通过创建ThreadLocal实例,我们可以为每个线程提供一个...

    ThreadLocal_ThreadLocal源码分析_源码.zip

    通过分析ThreadLocal的源码,我们可以深入理解其内部机制。例如,`initialValue()`方法用于提供默认的初始值,`set()`方法如何将值放入ThreadLocalMap,`get()`方法如何获取值,以及`remove()`方法如何清理线程局部...

    java ThreadLocal多线程专属的变量源码

    java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多...

    ThreadLocal 线程本地变量 及 源码分析.rar_开发_设计

    以上是对ThreadLocal的简单介绍和源码分析,实际使用中还需要结合具体业务场景进行合理设计和优化。通过深入理解ThreadLocal的工作原理,可以帮助我们更好地利用这一工具,提高代码的并发性能和可维护性。

    ThreadLocal 内存泄露的实例分析1

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

    对ThreadLocal的理解【源码分析+应用举例】

    ThreadLocal是JDK包提供的,它提供了线程本地变量,也就是如果你创建了一个ThreadLocal变量,那么访问这个变量的每一个线程都会有这个变量的一个本地副本。当多线程操作这个变量时,实际操作的就是自己本地内存里面...

    ThreadLocal

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

    ThreadLocal应用示例及理解

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

    JUC并发编程与源码分析视频课.zip

    《JUC并发编程与源码分析视频课》是一门深入探讨Java并发编程的课程,主要聚焦于Java Util Concurrency(JUC)库的使用和源码解析。JUC是Java平台提供的一组高级并发工具包,它极大地简化了多线程编程,并提供了更...

    ThreadLocal的原理,源码深度分析及使用.docx

    ThreadLocal 的原理、源码深度分析及使用 ThreadLocal 是 Java 语言中的一种机制,用于实现线程本地存储,能够帮助开发者在多线程环境下解决变量访问安全的问题。下面将对 ThreadLocal 的原理、实现机制、使用方法...

    druid 源码分析 逐层详解

    标题所指的知识点为“Druid 源码分析 逐层详解”,意味着我们需要深入分析Druid这一开源数据处理工具的源码,并从不同的层面揭示其内部实现机制。 首先,我们来看Druid的构架设计。Druid采用了分层的架构,每个层次...

Global site tag (gtag.js) - Google Analytics