ThreadLocal是用于解决多线程程序的并发问题的工具类,早在java 1.2版本中就提供了java.lang.ThreadLocal。
java在语言层面上没有为线程局部变量提供支持,但是java利用ThreadLocal类来支持线程局部变量。
ThreadLocal是一个保存线程本地化对象的容器。当运行与多线程环境中的某个对象使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程分配一个独立的变量副本。所以每个线程都可以独立改变自己的副本,而不会影响其他线程所对应的副本。从线程的角度看,这个变量就像线程专有的本地变量。
InheritableThreadLocal继承于ThreadLocal,它自动为子线程复制一份从父线程那里继承而来的本地变量;在创建子线程时,子线程会接收所有可继承的线程本地变量的初始值。当必须将本地线程变量自动传送给所有创建的子线程时,应尽可能地使用InheritableThreadLocal而非ThreadLocal。
ThreadLocal接口方法
- void set(Object value):设置当前线程的线程局部变量的值
- public Object get():返回当前线程所对应的线程局部变量
- public void remove():将当前线程局部变量的值删除,目的是为了减少内存的占用。该方法是java 5.0新增的。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显示调用该方法清除线程的局部变量并不是必需的操作,但它可以加快内存回收的速度。
- protected Object initialValue():返回该线程局部变量的初始值。该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第一次调用get()或者set(Object)时才执行,并且仅执行一次。ThreadLocal中的默认实现直接返回一个null。
在java 5.0中,ThreadLocal已经支持泛型。
ThreadLocal实现局部变量方法
在ThreadLocal类中有一个Map,用于存储每个线程的变量副本,Map中元素的键为线程对象,值为对应线程的变量副本。
下面的例子是一个简单的ThreadLocal实现(在思路上与ThreadLocal非常相似)
public class SimpleThreadLocal{
private Map valueMap = Collections.synchronizedMap(new HashMap());
public void set(Object newValue){
//键为线程对象,值为本线程的变量副本
valueMap.put(Thread.currentThread(),newValue);
}
public Object get(){
Thread currentThread = Thread.currentThread();
//返回本线程对应的变量
Object o = valueMap.get(currentThread);
//如果Map中不存在,则放入Map中保存
if(o==null && !valueMap.containsKey(currentThread)){
o=initialValue();
valueMap.put(currentThread,o);
}
return o;
}
public void remove() {
valueMap.remove(Thread.currentThread());
}
public Object initialValue() {
return null;
}
}
一个ThreadLocal实例
package com.jcl.base;
public class SequenceNumber{
//通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值
private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>(){
public Integer initialValue(){
return 0;
}
};
//获取下一个序列值
public int getNextNum(){ //获取下一个序列值
seqNum.set(seqNum.get()+1);
return seqNum.get();
}
public static void main(String[] args){
SequenceNumber sn=new SequenceNumber();
//3个线程共享sn,各自产生序列号
TestClient t1=new TestClient(sn);
TestClient t2=new TestClient(sn);
TestClient t3=new TestClient(sn);
t1.start();
t2.start();
t3.start();
}
private static class TestClient extends Thread{
private SequenceNumber sn;
public TestClient(SequenceNumber sn){
this.sn=sn;
}
public void run(){
//每个线程打印三个序列值
for(int i=0;i<3;i++){
System.out.println("thread["+Thread.currentThread().getName()+"] sn["+sn.getNextNum()+"]");
}
}
}
}