您的位置:首页 > 其它

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: