ThreadLocal的本质和应用分析
2015-02-26 17:46
225 查看
引言: 在Java的多线程编程中,竞争资源的同步是一个需要格外关注的问题。处理使用volatile和同步锁机制实现资源访问的一致性之外,还可以使用ThreadLocal来保存线程的私有变量,从而避免了竞争资源的产生。
1. ThreadLocal是什么?
ThreadLocal是服务于Thread的一种本地私有数据机制,threadlocalvariable(线程局部变量), 即为每一个使用该变量的线程都提供一个变量值的副本,与使用的线程绑定,每一个线程都可以独立地改变自己的副本,不会产生冲突。
其在Thread处于活跃状态,并且threadLocal对象可用的情况下,就可以在其中存放数据。在线程被销毁之后,则ThreadLocal中使用的资源和内存同时也被回收。
本质上,JVM通过ThreadLocal为多线程情况下,进行资源的访问提供了一种隔离机制,简化了编程的复杂度。
2. ThreadLocal的用法
2.1 ThreadLocal的主要方法
ThreadLocal() : 创建实例
T get() :返回当前线程中的值,如果未设置,则返回初始值
void remove() : 移除当前线程的值,并释放资源
void set(T value) :设置当前线程线程副本中的
protected void initialValue(): 用以在子类中被重写其初始值
由此可以看出此线程局部变量只可以存放一个对象。
2.2 ThreadLocal使用示例
[java] view
plaincopy
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;
}
}
}
从代码的运行结果来看,他们彼此之间没有什么相互的影响。
[java] view
plaincopy
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()方法的源代码
[java] view
plaincopy
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()函数的源代码:
[java] view
plaincopy
public void set(T value) {
Thread t = Thread.currentThread(); //线程对象实例
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value); //设值
else
createMap(t, value); // 创建,设置
}
基于以上的代码,我们可以清楚的发现,其实就是基于对象实例的Map实现。
6. 总结
ThreadLocal存放的内容,不支持基本数据类型,只支持对象类型。用以存放线程私有的数据,以规避繁琐的同步机制下的资源共享。一般而言,少量数据是可以通过这种简便的方式而实现线程访问的。这些数据不涉及到线程之间的通信和共享问题。
参考文档
1. http://lavasoft.blog.51cto.com/62575/51926/
2. http://docs.oracle.com/javase/6/docs/api/java/lang/ThreadLocal.html
1. ThreadLocal是什么?
ThreadLocal是服务于Thread的一种本地私有数据机制,threadlocalvariable(线程局部变量), 即为每一个使用该变量的线程都提供一个变量值的副本,与使用的线程绑定,每一个线程都可以独立地改变自己的副本,不会产生冲突。
其在Thread处于活跃状态,并且threadLocal对象可用的情况下,就可以在其中存放数据。在线程被销毁之后,则ThreadLocal中使用的资源和内存同时也被回收。
本质上,JVM通过ThreadLocal为多线程情况下,进行资源的访问提供了一种隔离机制,简化了编程的复杂度。
2. ThreadLocal的用法
2.1 ThreadLocal的主要方法
ThreadLocal() : 创建实例
T get() :返回当前线程中的值,如果未设置,则返回初始值
void remove() : 移除当前线程的值,并释放资源
void set(T value) :设置当前线程线程副本中的
protected void initialValue(): 用以在子类中被重写其初始值
由此可以看出此线程局部变量只可以存放一个对象。
2.2 ThreadLocal使用示例
[java] view
plaincopy
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;
}
}
}
从代码的运行结果来看,他们彼此之间没有什么相互的影响。
[java] view
plaincopy
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()方法的源代码
[java] view
plaincopy
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()函数的源代码:
[java] view
plaincopy
public void set(T value) {
Thread t = Thread.currentThread(); //线程对象实例
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value); //设值
else
createMap(t, value); // 创建,设置
}
基于以上的代码,我们可以清楚的发现,其实就是基于对象实例的Map实现。
6. 总结
ThreadLocal存放的内容,不支持基本数据类型,只支持对象类型。用以存放线程私有的数据,以规避繁琐的同步机制下的资源共享。一般而言,少量数据是可以通过这种简便的方式而实现线程访问的。这些数据不涉及到线程之间的通信和共享问题。
参考文档
1. http://lavasoft.blog.51cto.com/62575/51926/
2. http://docs.oracle.com/javase/6/docs/api/java/lang/ThreadLocal.html
相关文章推荐
- ThreadLocal的本质和应用分析
- 【Android应用源码分析】Java多线程:线程本地变量ThreadLocal源码分析
- ThreadLocal源码分析和应用
- IL应用之——用IL分析接口的本质
- ThreadLocal应用场景以及源码分析
- IL应用之——用IL分析接口的本质
- 多线程之ThreadLocal理解、应用及源码分析
- ThreadLocal应用场景以及源码分析
- 应用Level Set分析阔叶材年轮形态
- 2017云栖大会·杭州峰会:《在线用户行为分析:基于流式计算的数据处理及应用》实验环境准备
- python 金融应用(四)金融时间序列分析基础
- 【Android 应用开发】 Application 使用分析
- 分析sort命令在linux下的具体应用范围
- 回归分析方法应用实例2
- Maemo Linux手机平台系列分析:(17) Maemo应用开发: GNU make 与makefile
- JPEG 原理详细实例分析及其在嵌入式 Linux 中的应用
- Python抓取网页内容应用代码分析
- 深入分析linux内核及其应用(热力推荐!!)
- PCI标准及其应用的现状分析 (RT)
- iOS应用崩溃日志分析