Preface
参考:http://www.cnblogs.com/dolphin0520/p/3920407.html
在Java并发编程中,最主要问题就是对共享的资源如何进行合理的调配。因为多个线程访问同一个资源时难免会有临界区的冲突,一般的解决方式都是进行合理的加锁。加锁的一个最大问题就是对程序的效率打折扣。但有时候,线程之间访问的同一个资源,这个资源在各个线程中是相互独立的,也就是说,A线程拥有的同B线程的资源,即使A线程对该资源被修改或其他操作,不影响B线程中该资源的值。在JDK中,就有这么一个类用于专门应用这种情况:ThreadLocal。
顾名思义,该类的字面意思就是 线程本地,网上其他也叫线程本地变量存储线程本地存储。 ThreadLocal可以针对某个资源变量为每个线程创建一个属于自己的副本变量。
ThreadLocal使用
ThreadLocal有四个方法:
public T get() { }
public void set(T value) { }
public void remove() { }
protected T initialValue() { }
get()即获取该副本变量;set()即设置副本变量;remove()即删除该副本变量;
initialValue()该方法交由子类去实现,设置一个初值,当没有set()值,使用get()方法就获取到该从初值
举个例子
在Holder类中有一个变量value,就作为线程要访问的资源,指定该资源的set及get方法。如下:
static class Holder{
//为每个线程在内部都会创建一个属于自己的变量副本
private static final ThreadLocal threadLocalContext = new ThreadLocal();
//作为资源
String value;
public void setValue(String value){
threadLocalContext.set(value);
}
public String get(){
return threadLocalContext.get();
}
}
定义线程的简单的操作,即设置下value,获取一下value。
class MyThread extends Thread{
String value;
public MyThread(String value){
this.value = value;
}
@Override
public void run() {
holder.setValue(value);
System.out.println("测试线程的值:"+holder.get());
}
}
主线程也是同样的操作,采用主线程与测试线程并发的操作,如下:
@Test
public void test01() throws InterruptedException {
MyThread testThread = new MyThread("test");
holder.setValue("main");
System.out.println("main线程的值:"+holder.get());
testThread.start();
Thread.sleep(1000);
System.out.println("main线程的值:"+holder.get());
}
结果如下:
可见,一个线程对资源的改变并没有对另一个线程拥有的资源副本有影响。
一探究竟
ThreadLocalMap
进入ThreadLocal的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);
}
源码很简单,就是根据当前线程的ThreadLocalMap,而后set进去。如果map为空,就先用createMap方法先创建一个map。createMap源码如下:
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
发现创建一个Thread类中就有ThreadLocalMap这么一个变量,也就是说,每个线程都会维护ThreadLocalMap这么一个对象,既然是map,那么ThreadLocalMap就是存储着一些key、value的值。
ThreadLocalMap是ThreadLocal的一个内部类。其Entry的如下:
static class Entry extends WeakReference> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal> k, Object v) {
super(k);
value = v;
}
}
从该Entry发现,该Entry是以ThreadLocal对象本身作为变量,以实际的Object值作为value。此外,该Entry是WeakRefrence(弱引用)的子类,并且key就是是弱引用。
先不说这个弱引用。从之前的Demo中就可以看出,我们往ThreadLoca中set一个值后,这个值其实最后就是保存到了当前线程的副本中(ThreadLocalMap中),以当前的ThreadLocal对象作为key。通过ThreadLocal的get方法,再从当前线程的副本中取出该值。
进而能够保证,存储同一个的Oject的对象,不同线程保存到各自内部后,对该值的操作是相互独立的。
发表评论