一、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()); } } } }
相关推荐
**Java Development Kit (JDK) 1.6 中文API** JDK 1.6,也被称为Java SE 6,是Java平台标准版的一个重要版本,由Sun Microsystems(后被Oracle收购)发布。这个版本引入了许多增强功能和新特性,旨在提高开发效率、...
4. **并发与多线程**:JDK 1.6对并发库进行了大量优化,包括`java.util.concurrent`包中的工具类,如`ExecutorService`、`Future`、`Semaphore`等,以及`java.lang.ThreadLocal`的增强。 5. **NIO.2**:Java New I/...
《最实用的JDK1.6 API》是一个针对Java开发者的重要资源,它包含了JDK1.6版本的中文API文档,对于理解并高效利用Java语言进行软件开发具有极高的价值。JDK(Java Development Kit)是Java编程环境的核心组件,其中的...
该CHM文件包含了Java 1.6版本的关键内容,虽然描述中提到了"基于1.7api的函数说明",但实际上,它是关于JDK1.6的,因此应关注的是JDK1.6中的核心类库、接口和方法。在Java 1.6中,开发者可以找到诸如集合框架、IO流...
《Jdk+api+1.6+英文原版 CHM格式》是针对Java开发者的一份重要参考资料,它包含了Java Development Kit (JDK) 1.6版本的API文档,以CHM(Compiled Help Manual)格式呈现。CHM是一种微软开发的帮助文件格式,允许...
在JDK 8之前,这个内部Map是`ThreadLocal.ThreadLocalMap`,而从JDK 8开始,改为了`WeakReference<ThreadLocal<?>>`和`Object`的组合,提高了内存管理效率。 **JDK 8之前的ThreadLocalMap** 在JDK 8之前,...
Java并发编程中,ThreadLocal是一个非常重要的类,它是解决线程安全问题的一个工具。ThreadLocal提供了一种在多线程环境下使用“共享变量”的方式,但是它实际上是为每个线程提供一个变量的副本,这样每个线程都可以...
首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。各个线程中访问的是不同的对象。
ThreadLocal 源码分析和使用 ThreadLocal 是 Java 语言中的一种多线程编程机制,用于解决多线程程序的并发问题。它不是一个 Thread,而是一个 Thread 的局部变量。ThreadLocal 的出现是为了解决多线程程序中的共享...
**标题:“JDK的ThreadLocal理解(一)使用和测试”** **正文:** ThreadLocal是Java中的一个非常重要的线程安全工具类,它在多线程编程中扮演着独特的角色。通过创建ThreadLocal实例,我们可以为每个线程提供一个...
通过分析ThreadLocal的源码,我们可以深入理解其内部机制。例如,`initialValue()`方法用于提供默认的初始值,`set()`方法如何将值放入ThreadLocalMap,`get()`方法如何获取值,以及`remove()`方法如何清理线程局部...
java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多...
**JDK10与OpenJDK10源码解析** JDK10是Java开发工具包的第十个主要版本,它的发布标志着Java平台的持续进化。这个版本引入了许多新的特性和改进,旨在提升性能、易用性和开发者的生产力。本文将深入探讨JDK10的底层...
以上是对ThreadLocal的简单介绍和源码分析,实际使用中还需要结合具体业务场景进行合理设计和优化。通过深入理解ThreadLocal的工作原理,可以帮助我们更好地利用这一工具,提高代码的并发性能和可维护性。
在 `LeakingServlet` 的 `doGet` 方法中,如果 `ThreadLocal` 没有设置值,那么会创建一个新的 `MyCounter` 并设置到 `ThreadLocal` 中。关键在于,一旦 `MyCounter` 被设置到 `ThreadLocal`,那么它将与当前线程...
ThreadLocal是JDK包提供的,它提供了线程本地变量,也就是如果你创建了一个ThreadLocal变量,那么访问这个变量的每一个线程都会有这个变量的一个本地副本。当多线程操作这个变量时,实际操作的就是自己本地内存里面...
ThreadLocal是Java编程语言中的一个类,用于在多线程环境中提供线程局部变量。它是一种特殊类型的变量,每个线程都有自己的副本,互不影响,从而实现线程间数据隔离。ThreadLocal通常被用来解决线程共享数据时可能...
**线程局部变量(ThreadLocal)是Java编程中一个非常重要的工具类,它在多线程环境下提供了线程安全的数据存储。ThreadLocal并不是一个变量,而是一个类,它为每个线程都创建了一个独立的变量副本,使得每个线程都...
《JUC并发编程与源码分析视频课》是一门深入探讨Java并发编程的课程,主要聚焦于Java Util Concurrency(JUC)库的使用和源码解析。JUC是Java平台提供的一组高级并发工具包,它极大地简化了多线程编程,并提供了更...
ThreadLocal 的原理、源码深度分析及使用 ThreadLocal 是 Java 语言中的一种机制,用于实现线程本地存储,能够帮助开发者在多线程环境下解决变量访问安全的问题。下面将对 ThreadLocal 的原理、实现机制、使用方法...