from: http://blog.abreaking.com
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());
}
结果如下:
可见,一个线程对资源的改变并没有对另一个线程拥有的资源副本有影响。
反例
针对Holder类,如果不使用ThreadLocal,那么线程之间使用就是同一个资源了,该资源的修改肯定会影响其他线程了。如,我们直接使用最简单的set及get
static class Holder{
//作为资源
String value;
public void setValue(String value){
this.value = value;
}
public String get(){
return this.value;
}
}
其他部分不变,这时再查看结果:
应用场景
ThreadLocal使用最广泛的就是Session之间的数据源连接Connection,以及多数据源的管理。