1. ThreadLocal类的实现原理
ThreadLocal类是java中一种解决多线程环境下并发问题的新思路,它的原理就是为每一个线程提供一个变量的副本,这样每个线程对变量的修改就不会影响其他线程。
首先看一下ThreadLocal提供的API:
public
T get() { } //返回当前线程中变量的副本值。
public
void
set(T value) { } //设置当前线程的变量副本的值。
public
void
remove() { } //移除当前线程变量的值。
protected
T initialValue() { } //放回当前线程变量副本的初始值,是一个延迟加载方法,一般重写此方法。
public T get(){
Thread t = Thread.currentThread();
ThreadLoclMap map = getMap(t);
if(map != null){
ThreadLocalMap.Entry e = map.getEntry(this); //this 指当前的ThreadLocal
if(e != null)
return (T)e.value;
}
return setInitialValue();
}
//getMap()实际是获取Thread对象中的一个threadLocals变量,而该变量的类型是一个ThreadLocalMap 。
ThreadLocalMap getMap(Thread t){
return t.threadLocals;
}
在Thread中的threadLocals变量的定义如下:
ThreadLocal.ThreadLocalMap threadLocls = null;
而ThreadLocalMap 实际上是ThreadLocal的一个内部类它的定义如下:
static class ThreadLocalMap{
static class Entry extends WeakReference<ThreadLocal>{
Object value;
Entry(ThreadLocal k, Object v){
super(k);
value = v;
}
}
}
再看一下setInitialValue()的源码实现:
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;
}
createMap的实现如下:
void createMap(Thread t, T firstValue){
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
而set()方法实际上就是网当前Thread的ThreadLocalMap类型变量中存值。
从上面的源码可以看出实际上Thread类中有一个ThreadLocalMap类型的变量threadLocals,该变量存储以当前的ThreadLocal对象为key,以要保存的变量的值为value的键值对。而ThreadLocalMap是ThreadLocal的一个内部类,可以把它看做ThreadLocal中的一个Map,当在一个线程中调用ThreadLocal的get方法时,会取得当前线程中的ThreadLocalMap类型变量,如果该变量不为空,且里面有键值对,那么就直接取出该类map中的当前ThreadLocal对象为键锁对应的值,即当前线程中的变量值。如果该变量为空,那么再调用setInitialValue方法为其设置值,前提是重了initialValue方法,如果没有重写该方法,那么默认设置的值为null,如果在new一个ThreadLocal的时候重写了
initialValue方法,在调用setInitialValue就可以取得initialValue的值,现在明白重写initialValue的作用
了吧可以调用set方法设置值,其实设置值得过程就是存储副本的过程归根到底还是靠Thread对象中的类似map功能的ThreadLocalMap类型变量为每个线程存储变量的副本。这样每个线程都存储着一个变量的副本。
2.ThreadLocal的应用实例
public class Student {
private int age = 0;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class ThreadLocalTest implements Runnable{
private final static ThreadLocal stucentLocal = new ThreadLocal();
public static void main(String[] args) {
ThreadLocalTest tl = new ThreadLocalTest();
Thread t1 = new Thread(tl, "a");
Thread t2 = new Thread(tl, "b");
t1.start();
t2.start();
}
public void run(){
accessStudent();
}
public void accessStudent(){
String currentThreadName = Thread.currentThread().getName();
System.out.println(currentThreadName + "is running!");
Random r = new Random();
int age = r.nextInt(100);
Student student = getStudent();
student.setAge(age);
System.out.println("thread" + currentThreadName + "first read age is:" + student.getAge());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
protected Student getStudent(){
Student s = (Student) stucentLocal.get();
if(s == null){
s = new Student();
stucentLocal.set(s);
}
return s;
}
}
输出结果:
a is running!
b is running!
threadafirst read age is:17
threadbfirst read age is:50
可以看出,每个线程都保存自己的student的副本。