java.lang.ThreadLocal类研究
一、概述
ThreadLocal是什么呢?其实ThreadLocal并不是是一个线程的本地实现版本,它并非一个Thread,而是thread local variable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用很是简单,就是为每个使用该变量的线程都提供一个变量值的副本,是Java中一种较为特殊的线程绑定机制,是每个线程均可以独立地改变本身的副本,而不会和其它线程的副本冲突。
从线程的角度看,每一个线程都保持一个对其线程局部变量副本的隐式引用,只要线程是活动的而且 ThreadLocal 实例是可访问的;在线程消失以后,其线程局部实例的全部副本都会被垃圾回收(除非存在对这些副本的其余引用)。
经过ThreadLocal存取的数据,老是与当前线程相关,也就是说,JVM 为每一个运行的线程,绑定了私有的本地实例存取空间,从而为多线程环境常出现的并发访问问题提供了一种隔离机制。
ThreadLocal是如何作到为每个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每个线程的变量的副本。
归纳起来讲,对于多线程资源共享的问题,同步机制采用了"以时间换空间"的方式,而ThreadLocal采用了"以空间换时间"的方式。前者仅提供一份变量,让不一样的线程排队访问,然后者为每个线程都提供了一份变量,所以能够同时访问而互不影响。
二、API说明
ThreadLocal()
建立一个线程本地变量。
T get()
返回此线程局部变量的当前线程副本中的值,若是这是线程第一次调用该方法,则建立并初始化此副本。
protected T initialValue()
返回此线程局部变量的当前线程的初始值。最多在每次访问线程来得到每一个线程局部变量时调用此方法一次,即线程第一次使用 get() 方法访问变量的时候。若是线程先于 get 方法调用 set(T) 方法,则不会在线程中再调用 initialValue 方法。若该实现只返回 null;若是程序员但愿将线程局部变量初始化为 null 之外的某个值,则必须为 ThreadLocal 建立子类,并重写此方法。一般,将使用匿名内部类。initialValue 的典型实现将调用一个适当的构造方法,并返回新构造的对象。
void remove()
移除此线程局部变量的值。这可能有助于减小线程局部变量的存储需求。若是再次访问此线程局部变量,那么在默认状况下它将拥有其 initialValue。
void set(T value)
将此线程局部变量的当前线程副本中的值设置为指定值。许多应用程序不须要这项功能,它们只依赖于 initialValue() 方法来设置线程局部变量的值。
在程序中通常都重写initialValue方法,以给定一个特定的初始值。
三、示例
public class ThreadLocalTest {
private static ThreadLocal<String> val = new ThreadLocal<String>();
public static class Thread1 extends Thread {
private String name;
public Thread1(String name) {
this.name = name;
}
public void run() {
System.out.println(name + "初始值:" + val.get());
val.set("v[" + name + "]");
System.out.println(name + "设置后值:" + val.get());
}
}
public static class Thread2 extends Thread {
private String name;
public Thread2(String name) {
this.name = name;
}
public void run() {
System.out.println(name + "初始值:" + val.get());
val.set("v[" + name + "]");
System.out.println(name + "设置后值:" + val.get());
}
}
public static void main(String[] args) {
val.set("123");
System.out.println("主程序中设置值:" + val.get());
(new Thread1("A1")).start();
(new Thread1("A2")).start();
(new Thread2("B1")).start();
}
}
运行结果:
主程序中设置值:123
A1初始值:null
A1设置后值:v[A1]
B1初始值:null
A2初始值:null
B1设置后值:v[B1]
A2设置后值:v[A2]
四、总结
ThreadLocal使用场合主要解决多线程中数据数据因并发产生不一致问题。ThreadLocal为每一个线程的中并发访问的数据提供一个副本,经过访问副原本运行业务,这样的结果是耗费了内存,单大大减小了线程同步所带来性能消耗,也减小了线程并发控制的复杂度。
ThreadLocal不能使用原子类型,只能使用Object类型。ThreadLocal的使用比synchronized要简单得多。
ThreadLocal和Synchonized都用于解决多线程并发访问。可是ThreadLocal与synchronized有本质的区别。synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每个线程都提供了变量的副本,使得每一个线程在某一时间访问到的并非同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通讯时可以得到数据共享。
Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。
固然ThreadLocal并不能替代synchronized,它们处理不一样的问题域。Synchronized用于实现同步机制,比ThreadLocal更加复杂。