引言: 在Java的多线程编程中,竞争资源的同步是一个需要格外关注的问题。处理使用volatile和同步锁机制实现资源访问的一致性之外,还可以使用ThreadLocal来保存线程的私有变量,从而避免了竞争资源的产生。
1. ThreadLocal是什么?
ThreadLocal是服务于Thread的一种本地私有数据机制,threadlocalvariable(线程局部变量), 即为每一个使用该变量的线程都提供一个变量值的副本,与使用的线程绑定,每一个线程都可以独立地改变自己的副本,不会产生冲突。
其在Thread处于活跃状态,并且threadLocal对象可用的情况下,就可以在其中存放数据。在线程被销毁之后,则ThreadLocal中使用的资源和内存同时也被回收。
本质上,JVM通过ThreadLocal为多线程情况下,进行资源的访问提供了一种隔离机制,简化了编程的复杂度。
2. ThreadLocal的用法
2.1 ThreadLocal的主要方法
由此可以看出此线程局部变量只可以存放一个对象。
2.2 ThreadLocal使用示例
package org.test; public class ThreadLocalTest extends Thread { //必须的static final private final ThreadLocal<Student> mythreadLocal = new ThreadLocal<Student>(); private int age; public ThreadLocalTest(String name) { super(name); } public ThreadLocalTest(String name, int age) { super(name); this.age = age; } public static void main(String[] args) { ThreadLocalTest student1 = new ThreadLocalTest("thread-first",23); ThreadLocalTest student2 = new ThreadLocalTest("thread-second",30); student1.start(); student2.start(); } public void run() { testInfo(); } public void testInfo() { String currentThreadName = Thread.currentThread().getName(); System.out.println(currentThreadName + " is running!"); Student stud1 = this.getLocalVariable(); System.out.println("Student " + stud1.name + " is in age " + stud1.age + " in " + currentThreadName); } public Student getLocalVariable() { if (mythreadLocal.get() == null) { Student student = new Student(Thread.currentThread().getName() + "-student", this.age); mythreadLocal.set(student); } return mythreadLocal.get(); } public class Student { private String name; private int age; public Student(){} public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } }从代码的运行结果来看,他们彼此之间没有什么相互的影响。
Thread-1 is running! Thread-0 is running! Student LiSi is in age 30 in Thread-1 Student ZhangSan is in age 24 in Thread-0
注意: 这里的ThreadLocal按照JavaDoc文档的说明,应该声明为private static,因为它是在多个线程之间共享的一个数据结构,类似Map,以thread对象实例做为Key的。
3. ThreadLocal和同步锁机制的对比分析
概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
同步机制利用所实现资源的同步访问,确保某一个时刻只有一个线程在访问资源;而ThreadLoca则规避了同步,让每一个线程有自己的一份副本。
他们之间不能彼此替代,只是从不同的角度去解决线程访问资源的问题。threadLocal无法替代锁实现的资源共享,而锁也做不到可以提供给独立的线程实例资源。
4. 基于JVM的内存模型来分析ThreadLocal
ThreadLocal本质上是一个类似Map的结构,以各个线程对象本身为Key,将其值存放进去。这个结构在所有的基于同一个线程类创建出来的线程中被共享所有,就是只有一个,单例的对象。 虽然,使用get()方法来读取其值,但是默认的是使用当前的thread对象做为Key来检索的。
具体的实现机制可以参照源码分析。
5. ThreadLocal的源码分析
首先,来看看get()方法的源代码
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(); //如果为空,则设置初始值 }set()函数的源代码:
public void set(T value) { Thread t = Thread.currentThread(); //线程对象实例 ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); //设值 else createMap(t, value); // 创建,设置 }
6. 总结
ThreadLocal存放的内容,不支持基本数据类型,只支持对象类型。用以存放线程私有的数据,以规避繁琐的同步机制下的资源共享。一般而言,少量数据是可以通过这种简便的方式而实现线程访问的。这些数据不涉及到线程之间的通信和共享问题。
参考文档
1. http://lavasoft.blog.51cto.com/62575/51926/
2. http://docs.oracle.com/javase/6/docs/api/java/lang/ThreadLocal.html
原文:http://blog.csdn.net/blueheart20/article/details/19355713