首页 / 软件开发 / JAVA / Java多线程基础总结四:ThreadLocal
Java多线程基础总结四:ThreadLocal2012-06-23 iteye ftj20003说到ThreadLocal,首先说说这个类的命名。直观上看好像是个Thread的什么亲戚,但其实它想表达的意思是线程本地变量,也就是说每 个线程自己的变量。它作为一个JDK5以后支持范型的类,主要是想利用范型把非线程安全的共享变量,封装成绑定线程的安全不共享变量。 这样的解释我想我们多半能猜出它的实现思路:把一个共享变量在每个线程使用时,初始化一个副本,并且和线程绑定。以后所有的线程对 共享变量的操作都是对线程内部那个副本,完全的线程内部变量的操作。要实现这样功能类的设计,主要技术点是要能把副本和线程绑定映射,程序可以安全查找到当前线程的副本,修改后安全的绑定给线程 。所以我们想到了Map的存储结构,ThreadLocal内部就是使用了线程安全的Map形式的存储把currentThread和变量副本一一映射。既然要把共享的变成不共享的,那么就要变量满足一个场景:变量的状态不需要共享。例如无状态的bean在多线程之间是安全的,因为 线程之间不需要同步bean的状态,用了就走(很不负责啊),想用就用。但是对于有状态的bean在线程之间则必须小心,线程A刚看到状态 是a,正想利用a做事情,线程B把bean的状态改为了b,结果做了不该做的。但是如果有状态的bean不需要共享状态,每个线程看到状态a或 者b都可以做出自己的行为,这种情况下不同步的选择就是ThreadLocal了。利用ThreadLocal的优势就在于根本不用担心有状态的bean为了状态的一致而牺牲性能,去使用synchronized限制只有一个线程在同一时 间做出关于bean状态的行为。而是多个线程同时根据自己持有的bean的副本的状态做出行为,这样的转变对于并发的支持是那么的不可思议 。例如一个 Dao内有个Connection的属性,当多个线程使用Dao的同一个实例时,问题就来了:多个线程用一个Connection,而且它还是有 连接,关闭等等的状态转变的,我们很敏感的想到这个属性不安全!再看这个属性,其实它是多么的想告诉线程哥哥们:我的这些状态根本 就不想共享,不要因为我的状态而不敢一起追求。线程哥哥们也郁闷:你要是有多胞胎姐妹该多好啊!这时候ThreadLocal大哥过来说:小 菜,我来搞定!你们这些线程一人一个 Connection,你想关就关,想连接就连接,再也不用抱怨说它把你的连接关了。这样Dao的实例再也 不用因为自己有个不安全的属性而自卑了。当然 ThreadLocal的思路虽然是很好的,但是官方的说法是最初的实现性能并不好,随着Map结 构和Thread.currentThread的改进,性能较之synchronized才有了明显的优势。所以要是使用的是JDK1.2,JDK1.3等等,也不要妄想麻雀变 凤凰...再看ThreadLocal和synchronized的本质。前者不在乎多占点空间,但是绝对的忍受不了等待;后者对等待无所谓,但是就是不喜欢浪费 空间。这也反映出了算法的一个规律:通常是使用场景决定时间和空间的比例,既省时又省地的算法多数情况下只存在于幻想之中。下面写 个简单的例子解释一下,不过个人觉得设计的例子不太好,以后有实际的启发再替换吧。Java代码import java.util.concurrent.atomic.AtomicInteger;
/**
* User: yanxuxin
* Date: Dec 14, 2009
* Time: 9:26:41 PM
*/
public class ThreadLocalSample extends Thread {
private OperationSample2 operationSample;
public ThreadLocalSample(OperationSample2 operationSample) {
this.operationSample = operationSample;
}
@Override
public void run() {
operationSample.printAndIncrementNum();
}
public static void main(String[] args) {
final OperationSample2 operation = new OperationSample2();//The shared Object for threads.
for (int i = 0; i < 5; i++) {
new ThreadLocalSample(operation).start();
}
}
}
class OperationSample {
private int num;
//public synchronized void printAndIncrementNum() {
public void printAndIncrementNum() {
for (int i = 0; i < 2; i++) {
System.out.println(Thread.currentThread().getName() + "[id=" + num + "]");
num += 10;
}
}
}
class OperationSample2 {
private static ThreadLocal<Integer> threadArg = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 0;
}
};
public void printAndIncrementNum() {
for (int i = 0; i < 2; i++) {
int num = threadArg.get();
threadArg.set(num + 10);
System.out.println(Thread.currentThread().getName() + "[id=" + num + "]");
}
}
}
class OperationSample3 {
private static final AtomicInteger uniqueId = new AtomicInteger(0);
private static ThreadLocal<Integer> threadArg = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return uniqueId.getAndIncrement();
}
};
public void printAndIncrementNum() {
for (int i = 0; i < 2; i++) {
int num = threadArg.get();
threadArg.set(num + 10);
System.out.println(Thread.currentThread().getName() + "[id=" + num + "]");
}
}
}