ThreadLocal

首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。各个线程中访问的是不同的对象。 

另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本。通过ThreadLocal.set()将这个新创建的对象的引用保存到各线程的自己的一个map中,每个线程都有这样一个map,执行ThreadLocal.get()时,各线程从自己的map中取出放进去的对象,因此取出来的是各自自己线程中的对象,ThreadLocal实例是作为map的key来使用的。 

如果ThreadLocal.set()进去的东西本来就是多个线程共享的同一个对象,那么多个线程的ThreadLocal.get()取得的还是这个共享对象本身,还是有并发访问问题。 

下面来看一个hibernate中典型的ThreadLocal的应用:

Java代码
private static final ThreadLocal threadSession = new ThreadLocal();   
  1.   
  2. public static Session getSession() throws InfrastructureException {   
  3.     Session s = (Session) threadSession.get();   
  4.     try {   
  5.         if (s == null) {   
  6.             s = getSessionFactory().openSession();   
  7.             threadSession.set(s);   
  8.         }   
  9.     } catch (HibernateException ex) {   
  10.         throw new InfrastructureException(ex);   
  11.     }   
  12.     return s;   
  13. }  
    private static final ThreadLocal threadSession = new ThreadLocal();

    public static Session getSession() throws InfrastructureException {
        Session s = (Session) threadSession.get();
        try {
            if (s == null) {
                s = getSessionFactory().openSession();
                threadSession.set(s);
            }
        } catch (HibernateException ex) {
            throw new InfrastructureException(ex);
        }
        return s;
    }


可以看到在getSession()方法中,首先判断当前线程中有没有放进去session,如果还没有,那么通过sessionFactory().openSession()来创建一个session,再将session set到线程中,实际是放到当前线程的ThreadLocalMap这个map中,这时,对于这个session的唯一引用就是当前线程中的那个ThreadLocalMap(下面会讲到),而threadSession作为这个值的key,要取得这个session可以通过threadSession.get()来得到,里面执行的操作实际是先取得当前线程中的ThreadLocalMap,然后将threadSession作为key将对应的值取出。这个session相当于线程的私有变量,而不是public的。 
显然,其他线程中是取不到这个session的,他们也只能取到自己的ThreadLocalMap中的东西。要是session是多个线程共享使用的,那还不乱套了。 
试想如果不用ThreadLocal怎么来实现呢?可能就要在action中创建session,然后把session一个个传到service和dao中,这可够麻烦的。或者可以自己定义一个静态的map,将当前thread作为key,创建的session作为值,put到map中,应该也行,这也是一般人的想法,但事实上,ThreadLocal的实现刚好相反,它是在每个线程中有一个map,而将ThreadLocal实例作为key,这样每个map中的项数很少,而且当线程销毁时相应的东西也一起销毁了,不知道除了这些还有什么其他的好处。 

总之,ThreadLocal不是用来解决对象共享访问问题的,而主要是提供了保持对象的方法和避免参数传递的方便的对象访问方式。归纳了两点: 
1。每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。 
2。将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦。
 

当然如果要把本来线程共享的对象通过ThreadLocal.set()放到线程中也可以,可以实现避免参数传递的访问方式,但是要注意get()到的是那同一个共享对象,并发访问问题要靠其他手段来解决。但一般来说线程共享的对象通过设置为某类的静态变量就可以实现方便的访问了,似乎没必要放到线程中。 

ThreadLocal的应用场合,我觉得最适合的是按线程多实例(每个线程对应一个实例)的对象的访问,并且这个对象很多地方都要用到。 

下面来看看ThreadLocal的实现原理(jdk1.5源码)

Java代码
public class ThreadLocal<T> {   
  1.     /**  
  2.      * ThreadLocals rely on per-thread hash maps attached to each thread  
  3.      * (Thread.threadLocals and inheritableThreadLocals).  The ThreadLocal  
  4.      * objects act as keys, searched via threadLocalHashCode.  This is a  
  5.      * custom hash code (useful only within ThreadLocalMaps) that eliminates  
  6.      * collisions in the common case where consecutively constructed  
  7.      * ThreadLocals are used by the same threads, while remaining well-behaved  
  8.      * in less common cases.  
  9.      */  
  10.     private final int threadLocalHashCode = nextHashCode();   
  11.   
  12.     /**  
  13.      * The next hash code to be given out. Accessed only by like-named method.  
  14.      */  
  15.     private static int nextHashCode = 0;   
  16.   
  17.     /**  
  18.      * The difference between successively generated hash codes - turns  
  19.      * implicit sequential thread-local IDs into near-optimally spread  
  20.      * multiplicative hash values for power-of-two-sized tables.  
  21.      */  
  22.     private static final int HASH_INCREMENT = 0x61c88647;   
  23.   
  24.     /**  
  25.      * Compute the next hash code. The static synchronization used here  
  26.      * should not be a performance bottleneck. When ThreadLocals are  
  27.      * generated in different threads at a fast enough rate to regularly  
  28.      * contend on this lock, memory contention is by far a more serious  
  29.      * problem than lock contention.  
  30.      */  
  31.     private static synchronized int nextHashCode() {   
  32.         int h = nextHashCode;   
  33.         nextHashCode = h + HASH_INCREMENT;   
  34.         return h;   
  35.     }   
  36.   
  37.     /**  
  38.      * Creates a thread local variable.  
  39.      */  
  40.     public ThreadLocal() {   
  41.     }   
  42.   
  43.     /**  
  44.      * Returns the value in the current thread's copy of this thread-local  
  45.      * variable.  Creates and initializes the copy if this is the first time  
  46.      * the thread has called this method.  
  47.      *  
  48.      * @return the current thread's value of this thread-local  
  49.      */  
  50.     public T get() {   
  51.         Thread t = Thread.currentThread();   
  52.         ThreadLocalMap map = getMap(t);   
  53.         if (map != null)   
  54.             return (T)map.get(this);   
  55.   
  56.         // Maps are constructed lazily.  if the map for this thread   
  57.         // doesn't exist, create it, with this ThreadLocal and its   
  58.         // initial value as its only entry.   
  59.         T value = initialValue();   
  60.         createMap(t, value);   
  61.         return value;   
  62.     }   
  63.   
  64.     /**  
  65.      * Sets the current thread's copy of this thread-local variable  
  66.      * to the specified value.  Many applications will have no need for  
  67.      * this functionality, relying solely on the {@link #initialValue}  
  68.      * method to set the values of thread-locals.  
  69.      *  
  70.      * @param value the value to be stored in the current threads' copy of  
  71.      *        this thread-local.  
  72.      */  
  73.     public void set(T value) {   
  74.         Thread t = Thread.currentThread();   
  75.         ThreadLocalMap map = getMap(t);   
  76.         if (map != null)   
  77.             map.set(this, value);   
  78.         else  
  79.             createMap(t, value);   
  80.     }   
  81.   
  82.     /**  
  83.      * Get the map associated with a ThreadLocal. Overridden in  
  84.      * InheritableThreadLocal.  
  85.      *  
  86.      * @param  t the current thread  
  87.      * @return the map  
  88.      */  
  89.     ThreadLocalMap getMap(Thread t) {   
  90.         return t.threadLocals;   
  91.     }   
  92.   
  93.     /**  
  94.      * Create the map associated with a ThreadLocal. Overridden in  
  95.      * InheritableThreadLocal.  
  96.      *  
  97.      * @param t the current thread  
  98.      * @param firstValue value for the initial entry of the map  
  99.      * @param map the map to store.  
  100.      */  
  101.     void createMap(Thread t, T firstValue) {   
  102.         t.threadLocals = new ThreadLocalMap(this, firstValue);   
  103.     }   
  104.   
  105.     .......   
  106.   
  107.     /**  
  108.      * ThreadLocalMap is a customized hash map suitable only for  
  109.      * maintaining thread local values. No operations are exported  
  110.      * outside of the ThreadLocal class. The class is package private to  
  111.      * allow declaration of fields in class Thread.  To help deal with  
  112.      * very large and long-lived usages, the hash table entries use  
  113.      * WeakReferences for keys. However, since reference queues are not  
  114.      * used, stale entries are guaranteed to be removed only when  
  115.      * the table starts running out of space.  
  116.      */  
  117.     static class ThreadLocalMap {   
  118.   
  119.     ........   
  120.   
  121.     }   
  122.   
  123. }  
public class ThreadLocal<T> {
    /**
     * ThreadLocals rely on per-thread hash maps attached to each thread
     * (Thread.threadLocals and inheritableThreadLocals).  The ThreadLocal
     * objects act as keys, searched via threadLocalHashCode.  This is a
     * custom hash code (useful only within ThreadLocalMaps) that eliminates
     * collisions in the common case where consecutively constructed
     * ThreadLocals are used by the same threads, while remaining well-behaved
     * in less common cases.
     */
    private final int threadLocalHashCode = nextHashCode();

    /**
     * The next hash code to be given out. Accessed only by like-named method.
     */
    private static int nextHashCode = 0;

    /**
     * The difference between successively generated hash codes - turns
     * implicit sequential thread-local IDs into near-optimally spread
     * multiplicative hash values for power-of-two-sized tables.
     */
    private static final int HASH_INCREMENT = 0x61c88647;

    /**
     * Compute the next hash code. The static synchronization used here
     * should not be a performance bottleneck. When ThreadLocals are
     * generated in different threads at a fast enough rate to regularly
     * contend on this lock, memory contention is by far a more serious
     * problem than lock contention.
     */
    private static synchronized int nextHashCode() {
        int h = nextHashCode;
        nextHashCode = h + HASH_INCREMENT;
        return h;
    }

    /**
     * Creates a thread local variable.
     */
    public ThreadLocal() {
    }

    /**
     * Returns the value in the current thread's copy of this thread-local
     * variable.  Creates and initializes the copy if this is the first time
     * the thread has called this 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)
            return (T)map.get(this);

        // Maps are constructed lazily.  if the map for this thread
        // doesn't exist, create it, with this ThreadLocal and its
        // initial value as its only entry.
        T value = initialValue();
        createMap(t, value);
        return value;
    }

    /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Many applications will have no need for
     * this functionality, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current threads' 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 the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

    /**
     * Create the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the map
     * @param map the map to store.
     */
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

    .......

    /**
     * ThreadLocalMap is a customized hash map suitable only for
     * maintaining thread local values. No operations are exported
     * outside of the ThreadLocal class. The class is package private to
     * allow declaration of fields in class Thread.  To help deal with
     * very large and long-lived usages, the hash table entries use
     * WeakReferences for keys. However, since reference queues are not
     * used, stale entries are guaranteed to be removed only when
     * the table starts running out of space.
     */
    static class ThreadLocalMap {

    ........

    }

}



可以看到ThreadLocal类中的变量只有这3个int型:

Java代码
private final int threadLocalHashCode = nextHashCode();   
  1. private static int nextHashCode = 0;   
  2. private static final int HASH_INCREMENT = 0x61c88647;  
    private final int threadLocalHashCode = nextHashCode();
    private static int nextHashCode = 0;
    private static final int HASH_INCREMENT = 0x61c88647;


而作为ThreadLocal实例的变量只有 threadLocalHashCode 这一个,nextHashCode 和HASH_INCREMENT 是ThreadLocal类的静态变量,实际上HASH_INCREMENT是一个常量,表示了连续分配的两个ThreadLocal实例的threadLocalHashCode值的增量,而nextHashCode 的表示了即将分配的下一个ThreadLocal实例的threadLocalHashCode 的值。 

可以来看一下创建一个ThreadLocal实例即new ThreadLocal()时做了哪些操作,从上面看到构造函数ThreadLocal()里什么操作都没有,唯一的操作是这句:

Java代码
private final int threadLocalHashCode = nextHashCode();  
    private final int threadLocalHashCode = nextHashCode();


那么nextHashCode()做了什么呢:

Java代码
private static synchronized int nextHashCode() {   
  1.     int h = nextHashCode;   
  2.     nextHashCode = h + HASH_INCREMENT;   
  3.     return h;   
  4. }  
    private static synchronized int nextHashCode() {
        int h = nextHashCode;
        nextHashCode = h + HASH_INCREMENT;
        return h;
    }

就是将ThreadLocal类的下一个hashCode值即nextHashCode的值赋给实例的threadLocalHashCode,然后nextHashCode的值增加HASH_INCREMENT这个值。 

因此ThreadLocal实例的变量只有这个threadLocalHashCode,而且是final的,用来区分不同的ThreadLocal实例,ThreadLocal类主要是作为工具类来使用,那么ThreadLocal.set()进去的对象是放在哪儿的呢? 

看一下上面的set()方法,两句合并一下成为

Java代码
ThreadLocalMap map = Thread.currentThread().threadLocals;  
        ThreadLocalMap map = Thread.currentThread().threadLocals;


这个ThreadLocalMap 类是ThreadLocal中定义的内部类,但是它的实例却用在Thread类中:

Java代码
public class Thread implements Runnable {   
  1.     ......   
  2.   
  3.     /* ThreadLocal values pertaining to this thread. This map is maintained  
  4.      * by the ThreadLocal class. */  
  5.     ThreadLocal.ThreadLocalMap threadLocals = null;     
  6.     ......   
  7. }  
public class Thread implements Runnable {
    ......

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;  
    ......
}



再看这句:

Java代码
if (map != null)   
  1.     map.set(this, value);  
        if (map != null)
            map.set(this, value);


也就是将该ThreadLocal实例作为key,要保持的对象作为值,设置到当前线程的ThreadLocalMap 中,get()方法同样大家看了代码也就明白了,ThreadLocalMap 类的代码太多了,我就不帖了,自己去看源码吧。


介绍

线程程序介绍

早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。

关于其变量

ThreadLocal很容易让人望文生义,想当然地认为是一个“本地线程”。其实,ThreadLocal并不是一个Thread,而是Thread的 局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些。
所以,在Java中编写线程 局部变量的代码相对来说要笨拙一些,因此造成线程局部变量没有在Java开发者中得到很好的普及。
ThreadLocal的接口方法
ThreadLocal类接口很简单,只有4个方法,我们先来了解一下:
void set(Object value)
public void remove()
将当前线程 局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 1.5 新增的方法。需要指出的是,当线程结束后,对应该线程的 局部变量将自动被 垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
protected Object initialValue()
返回该线程 局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在 线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。
值得一提的是,在JDK5.0中,ThreadLocal已经支持 泛型,该类的类名已经变为ThreadLocal<T>。API方法也相应进行了调整,新版本的API方法分别是void set(T value)、T get()以及T initialValue()。
ThreadLocal是如何做到为每一个线程维护 变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中定义了一个ThreadLocalMap,每一个Thread中都有一个该类型的变量——threadLocals——用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。

2线程

To keep state with a thread (user-id, transaction-id, logging-id) To cache objects which you need frequently ThreadLocal类
它主要由四个方法组成initialValue(),get(),set(T),remove(),其中值得注意的是initialValue(),该方法是一个protected的方法,显然是为了子类重写而特意实现的。该方法返回当前线程在该线程 局部变量的初始值,这个方法是一个延迟调用方法,在一个线程第1次调用get()时才执行,并且仅执行1次(即:最多在每次访问线程来获得每个线程局部变量时调用此方法一次,即线程第一次使用get()方法访问变量的时候。如果线程先于get方法调用set(T)方法,则不会在线程中再调用initialValue方法)。ThreadLocal中的缺省实现直接返回一个null:

举例

ThreadLocal的原理
在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。比如下面的示例实现:
public class ThreadLocal
private Map values = Collections.synchronizedMap(new HashMap());
public Object get()
{
Thread curThread = Thread.currentThread();
Object o = values.get(curThread);
if (o == null && !values.containsKey(curThread))
{
o = initialValue();
values.put(curThread, o);
}
return o;
values.put(Thread.currentThread(), newValue);
}
public Object initialValue()
{
return null;
}
}

使用方法

ThreadLocal 的使用
使用方法一:
Hibernate的文档时看到了关于使ThreadLocal管理多线程访问的部分。具体代码如下
1. public static final ThreadLocal session = new ThreadLocal();
2. public static Session currentSession() {
3. Session s = (Session)session.get();
4. //open a new session,if this session has none
5. if(s == null){
6. s = sessionFactory.openSession();
7. session.set(s);
8. }
return s;
9. }
我们逐行分析
1。 初始化一个ThreadLocal对象,ThreadLocal有三个成员方法 get()、set()、initialvalue()。
如果不初始化initialvalue,则initialvalue返回null。
3。session的get根据当前线程返回其对应的线程内部 变量,也就是我们需要的net.sf.hibernate.Session(相当于对应每个数据库连接).多线程情况下共享数据库链接是不安全的。ThreadLocal保证了每个线程都有自己的s(数据库连接)。
5。如果是该线程初次访问,自然,s(数据库连接)会是null,接着创建一个Session,具体就是行6。
6。创建一个数据库连接实例 s
7。保存该数据库连接s到ThreadLocal中。
8。如果当前线程已经访问过数据库了,则从session中get()就可以获取该线程上次获取过的连接实例。
使用方法二
当要给线程初始化一个特殊值时,需要自己实现ThreadLocal的子类并重写该方法,通常使用一个内部匿名类对ThreadLocal进行 子类化,EasyDBO中创建jdbc连接上下文就是这样做的:
public class JDBCContext{
private static Logger logger = Logger.getLogger(JDBCContext.class);
private DataSource ds;
protected Connection connection;
}
public static JDBCContext getJdbcContext(javax.sql.DataSource ds)
{
if(jdbcContext==null)jdbcContext=new JDBCContextThreadLocal(ds);
JDBCContext context = (JDBCContext) jdbcContext.get();
if (context == null) {
context = new JDBCContext(ds);
}
return context;
}
private static class JDBCContextThreadLocal extends ThreadLocal {
public javax.sql.DataSource ds;
public JDBCContextThreadLocal(javax.sql.DataSource ds)
{
this.ds=ds;
}
protected synchronized Object initialValue() {
return new JDBCContext(ds);
}
}
}
简单的实现版本
代码清单1 SimpleThreadLocal
public class SimpleThreadLocal {
private Map valueMap = Collections.synchronizedMap(new HashMap());
public void set(Object newValue) {
valueMap.put(Thread.currentThread(), newValue);①键为线程对象,值为本线程的 变量副本
}
public Object get() {
valueMap.put(currentThread, o);
}
return o;
}
public void remove() {
valueMap.remove(Thread.currentThread());
}
public Object initialValue() {
return null;
}
}
虽然代码清单9‑3这个ThreadLocal实现版本显得比较幼稚,但它和JDK所提供的ThreadLocal类在实现思路上是相近的。

3实例

举例

下面,我们通过一个具体的实例了解一下ThreadLocal的具体使用方法。
代码清单2 SequenceNumber
package com.baobaotao.basic;
public class SequenceNumber {
①通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值
private static ThreadLocal seqNum = new ThreadLocal(){
public Integer initialValue(){
return 0;
}
};
②获取下一个序列值
public int getNextNum(){
seqNum.set((Integer)seqNum.get()+1);
return (Integer)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);
private SequenceNumber sn;
public TestClient(SequenceNumber sn) {
this. sn = sn;
}
public void run()
{
for (int i = 0; i < 3; i++) {④每个线程打出3个序列值
System.out.println("thread["+Thread.currentThread().getName()+
"] sn["+sn.getNextNum()+"]");
}
}
}
}

分析

通常我们通过匿名内部类的方式定义ThreadLocal的子类,提供初始的 变量值,如例子中①处所示。TestClient线程产生一组序列号,在③处,我们生成3个TestClient,它们共享同一个SequenceNumber实例。运行以上代码,在控制台上输出以下的结果:
thread[Thread-2] sn[1]
thread[Thread-0] sn[1]
thread[Thread-1] sn[1]
thread[Thread-2] sn[2]
thread[Thread-0] sn[2]
thread[Thread-1] sn[2]
thread[Thread-2] sn[3]
thread[Thread-0] sn[3]
thread[Thread-1] sn[3]
考察输出的结果信息,我们发现每个 线程所产生的序号虽然都共享同一个SequenceNumber实例,但它们并没有发生相互干扰的情况,而是各自产生独立的序列号,这是因为我们通过ThreadLocal为每一个线程提供了单独的副本。

4场景

说明

在Java的多线程编程中,为保证多个线程对共享 变量的安全访问,通常会使用synchronized来保证同一时刻只有一个线程对共享变量进行操作。这种情况下可以将 类变量放到ThreadLocal类型的对象中,使变量在每个线程中都有独立拷贝,不会出现一个线程读取变量时而被另一个线程修改的现象。
下面举例说明:
public class QuerySvc {
private static ThreadLocal sqlHolder = new ThreadLocal();
public QuerySvc() {
}
public void execute() {
System.out.println("Thread " + Thread.currentThread().getId() +" Sql is " +sqlHolder.get());
System.out.println("Thread " + Thread.currentThread().getId() +" Thread Local variable Sql is " + sqlHolder.get());
}
public void setSql(String sql) {
sqlHolder.set(sql);
}
}

多线程访问

为了说明多线程访问对于 类变量和ThreadLocal变量的影响,QuerySvc中分别设置了类变量sql和ThreadLocal变量,这种场景类似web应用中多个请求线程携带不同查询条件对一个servlet实例的访问,然后servlet调用 业务对象,并传入不同查询条件,最后要保证每个请求得到的结果是对应的查询条件的结果。
使用QuerySvc的工作线程如下:
public class Work extends Thread {
private QuerySvc querySvc;
private String sql;
public Work(QuerySvc querySvc,String sql) {
this.querySvc = querySvc;
this.sql = sql;
}
public void run() {
querySvc.setSql(sql);
querySvc.execute();
}
}
运行线程代码如下:
QuerySvc qs = new QuerySvc();
for (int k=0; k<10; k++)
String sql = "Select * from table where id =" + k;
new Work(qs,sql).start();
}
先创建一个QuerySvc实例对象,而ThreadLocal中的值总是和set中设置的值一样,这样通过使用ThreadLocal获得了 线程安全性
如果一个对象要被多个线程访问,而该对象存在 类变量被不同类方法读写,为获得 线程安全,可以用ThreadLocal来替代类变量。
Thread同步机制的比较

说明

ThreadLocal和 线程同步机制相比有什么优势呢?ThreadLocal和线程同步机制都是为了解决多线程中相同 变量的访问冲突问题。
在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该 变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放 对象锁等繁杂的问题, 程序设计和编写难度相对较大。
而ThreadLocal则从另一个角度来解决 多线程的并发访问。在编写 多线程代码时,可以把不安全的 变量封装进ThreadLocal。
由于ThreadLocal中可以持有任何类型的对象,低版本JDK所提供的get()返回的是Object对象,需要 强制类型转换。但JDK 5.0通过 泛型很好的解决了这个问题,在一定程度地简化ThreadLocal的使用,代码清单 9 2就使用了JDK 5.0新的ThreadLocal<T>版本。
概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
Spring使用ThreadLocal解决线程安全问题
我们知道在一般情况下,TransactionSynchronizationManager、LocaleContextHolder等)中非 线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在 多线程中共享了。
一般的Web应用划分为展现层、服务层和 持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用。在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程,如图9‑2所示:
9-2

9-2

图1同一线程贯通三层
这样你就可以根据需要,将一些非 线程安全变量以ThreadLocal存放,在同一次请求响应的调用线程中,所有关联的对象引用到的都是同一个 变量
下面的实例能够体现Spring对有状态Bean的改造思路:
代码清单3 TopicDao:非 线程安全
public class TopicDao {
private Connection conn;①一个非 线程安全变量
public void addTopic(){
Statement stat = conn.createStatement();②引用非 线程安全 变量
}
}
由于①处的conn是 成员变量,因为addTopic()方法是非 线程安全的,必须在使用时创建一个新TopicDao实例(非singleton)。下面使用ThreadLocal对conn这个非 线程安全的“状态”进行改造:
代码清单4 TopicDao: 线程安全
import java.sql.Connection;
import java.sql.Statement;
public class TopicDao {
①使用ThreadLocal保存Connection 变量
private static ThreadLocal<Connection> connThreadLocal = new ThreadLocal<Connection>();
public static Connection getConnection(){
②如果connThreadLocal没有本线程对应的Connection创建一个新的Connection,
并将其保存到线程本地 变量中。
}
public void addTopic() {
④从ThreadLocal中获取线程对应的Connection
Statement stat = getConnection().createStatement();
}
}
不同的 线程在使用TopicDao时,这样,就保证了不同的 线程使用线程相关的Connection,而不会使用其它线程的Connection。因此,这个TopicDao就可以做到singleton共享了。
当然,这个例子本身很粗糙,将Connection的ThreadLocal直接放在DAO只能做到本DAO的多个方法共享Connection时不发生线程安全问题,但无法和其它DAO共用同一个Connection,要做到同一 事务多DAO共享同一Connection,必须在一个共同的外部类使用ThreadLocal保存Connection。
小结

解决方法

ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的 变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值