基本概念
Java中,多线程并发控制有多种方式,一种是实用synchronized关键字,互斥访问关键代码;另一种是实用CAS思路,进行乐观的并发。关于并发控制,下文会提到。synchronized是采用加锁的办法控制并发;CAS是先假设可以并发,当发现数据脏读的时候,回滚执行,比如AtomicInteger。
这里提供了一种新的思路hreadLocal。ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,故线程对变量的修改不会影响其他线程的执行。显然,在变量共享的时候,应该使用前两种;而线程独立进行,互不依赖关联的时候,才能用ThreadLocal。前者以时间换空间,而ThreadLocal以空间换时间。
简单说ThreadLocal侧重于对每个线程,保存线程自身的状态,从而实现并发。如ThrealLocal文档中所述:
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via itsget or set method) has its own, independently initialized copy of the variable.ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).
For example, the class below generates unique identifiers local to each thread. A thread's id is assigned the first time it invokesThreadId.get() and remains unchanged on subsequent calls.
import java.util.concurrent.atomic.AtomicInteger; public class ThreadId { // Atomic integer containing the next thread ID to be assigned private static final AtomicInteger nextId = new AtomicInteger(0); // Thread local variable containing each thread's ID private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() { @Override protected Integer initialValue() { return nextId.getAndIncrement(); } }; // Returns the current thread's unique ID, assigning it if necessary public static int get() { return threadId.get(); } }
Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and theThreadLocal instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).
方法
ThreadLocal类接口很简单,只有4个方法,我们先来了解一下:void set(Object value)
设置当前线程的线程局部变量的值。
public Object get()
该方法返回当前线程所对应的线程局部变量。
public void remove()
将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
protected Object initialValue()
返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了覆盖而设计的。这个方法是一个延迟调用方法,在线程调用get()时,如果没有找到存储的变量,就调用这个函数,生成默认的初始变量。ThreadLocal中的缺省实现直接返回一个null。
值得一提的是,在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>。API方法也相应进行了调整,新版本的API方法分别是void set(T value)、T get()以及T initialValue()。
实现
在ThreadLocal使用线程自身的ThreadLocalMap,存储每一个变量的副本,ThreadLocalMap中元素的键为线程的ThreadLocal,值为object,对应线程的变量副本。
考虑到线程并发可能数量巨大,所以ThreadLocalMap的键采用弱引用,这样便不会因为大量的键存在而造成内存空间的不足。
同时,ThreadLocalMap的hash碰撞机制使线性探测模型,hash值由每个Threadlocal变量自己保存于int threadLocalHashCode变量中。
set方法。
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
get方法。
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
remove和initialValue略。
使用
文档中说明:ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread。即将线程和线程相关的变量关联起来。如果我们希望通过某个类将状态(例如用户ID、事务ID)与线程关联起来,那么通常在这个类中定义private static类型的ThreadLocal 实例。
例如Hibernate获取session:
public class HibernateUtil {
private static Log log = LogFactory.getLog(HibernateUtil.class);
private static final SessionFactory sessionFactory; //定义SessionFactory
static {
try {
// 通过默认配置文件hibernate.cfg.xml创建SessionFactory
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
log.error("初始化SessionFactory失败!", ex);
throw new ExceptionInInitializerError(ex);
}
}
//创建线程局部变量session,用来保存Hibernate的Session
public static final ThreadLocal session = new ThreadLocal();
/**
* 获取当前线程中的Session
* @return Session
* @throws HibernateException
*/
public static Session currentSession() throws HibernateException {
Session s = (Session) session.get();
// 如果Session还没有打开,则新开一个Session
if (s == null) {
s = sessionFactory.openSession();
session.set(s); //将新开的Session保存到线程局部变量中
}
return s;
}
public static void closeSession() throws HibernateException {
//获取线程局部变量,并强制转换为Session类型
Session s = (Session) session.get();
session.set(null);
if (s != null)
s.close();
}
}
同时,如果一个线程需要多个变量,那么,一种方法就是用一个对象去具体保存这些值;另一个办法是用多个ThreadLocal变量分别保存这些变量。
对于ThreadLocalMap的实现,我觉得默认生成16个桶不太合理;大部分情况,一个ThreadLocalMap里只保存一个变量。
参考:
http://blog.csdn.net/vking_wang/article/details/14225379