设计模式

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

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

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

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

  1. private static final ThreadLocal threadSession = new ThreadLocal();
  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. }

可以看到在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源码)

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

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

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

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

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

  1. private final int threadLocalHashCode = nextHashCode();

那么nextHashCode()做了什么呢:

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

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

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

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

  1. ThreadLocalMap map = Thread.currentThread().threadLocals;

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

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

再看这句:

  1. if (map != null)
  2. map.set(this, value);

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

在spring+beranate中多数据源中使用 ThreadLocal ,总结的原理 --费元星的更多相关文章

  1. linux shell 中"2>&1"含义-完美理解-费元星

    笨鸟先飞,先理解.   脚本是:      nohup /mnt/Nand3/H2000G  >/dev/null  2>&1  &      对于& 1 更准确的 ...

  2. 在编程的时候,NotePad++ 中闪烁的光标突然有竖着闪烁的编程蓝色下划线闪烁的--小技巧告诉你-费元星

    当在写代码时出现的光标闪烁(横线闪烁) 在键盘上找 Insert ,按这个Insert就可以把横向闪烁光标( _ )修改成竖向闪烁光标样式( | ),横向光标会在你写代码的时候修改前面的代码,把光标移 ...

  3. Spring中配置数据源的4种形式

    不管采用何种持久化技术,都需要定义数据源.Spring中提供了4种不同形式的数据源配置方式: spring自带的数据源(DriverManagerDataSource),DBCP数据源,C3P0数据源 ...

  4. Spring中配置数据源的4种形式(转)

    原文http://blog.csdn.net/orclight/article/details/8616103       不管采用何种持久化技术,都需要定义数据源.Spring中提供了4种不同形式的 ...

  5. Spring中配置数据源的四种方式

    1.spring自带的数据源 <bean id="dataSource" class="org.springframework.jdbc.datasource.Dr ...

  6. Spring中配置数据源常用的形式

    不管采用何种持久化技术,都需要定义数据源.Spring中提供了4种不同形式的数据源配置方式: spring自带的数据源(DriverManagerDataSource),DBCP数据源,C3P0数据源 ...

  7. spring中配置数据源

    spring中配置数据源的几种常见方式: #mysql 数据库配置(jdbc.properties) jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.u ...

  8. 【转】Spring总结以及在面试中的一些问题

    [转]Spring总结以及在面试中的一些问题. 1.谈谈你对spring IOC和DI的理解,它们有什么区别? IoC Inverse of Control 反转控制的概念,就是将原本在程序中手动创建 ...

  9. Spring总结以及在面试中的一些问题

    Spring总结以及在面试中的一些问题. 1.谈谈你对spring IOC和DI的理解,它们有什么区别? IoC Inverse of Control 反转控制的概念,就是将原本在程序中手动创建Use ...

随机推荐

  1. 使用 Android 客户端向 Ruby on rails 构建的 Web Application 提交 HTTP GET 和 HTTP POST 请求

    最近想弄个能访问 Internet 的 Android 应用,因为求快所以用了 Ruby on Rails 来提供 HTTP 资源.这方面的资料还是比较少的,所以把尝试的过程记录下来. 1 使用 Ru ...

  2. sql语句更新某字段内容中部分数据

    使用到的sql 语句的关键字就是replace, 如下图,把带有zhangjun 的值替换成 user 使用的sql语句就是 update 表名 set 字段名=replace(字段名,‘替换字符内容 ...

  3. react中 props,state与render函数的关系

    我们很明显的能够感受到,react是一门数据驱动的框架,当数据发生变化,页面就会自动发生变化,他背后的原理是怎么样子的呢 比如todolist例子里面,inputValue变了,框里面的内容就会自动变 ...

  4. 3676: [Apio2014]回文串

    3676: [Apio2014]回文串 Time Limit: 20 Sec Memory Limit: 128 MB Submit: 1740 Solved: 744 [Submit][Status ...

  5. 【转】onConfigurationChanged

    http://blog.csdn.net/xiaodongvtion/article/details/679938 转载自该文章. 注意:onConfigurationChanged事件并不是只有屏幕 ...

  6. qbxt Day4

    1.树形dp 例题1 树上最长链 其实有两种方法,但为了简便,就只学了最通用的dp算法 我们考虑设dp[i][0/1]表示以i为根的最长路和次长路,然后拼接就行了 第二维0表示最长路,1表示次长路 i ...

  7. 总结的MR中连接操作

    1 reduce side join在map端加上标记, 在reduce容器保存,然后作笛卡尔积缺点: 有可能oom 2 map side join  2.1 利用内存和分布式缓存,也有oom风险 2 ...

  8. Page 由于代码已经过优化或者本机框架位于调用堆栈之上

    Page.Response.Clear();            Page.Response.Write("<script type=\"text/javascript\& ...

  9. LeetCode5.最长回文子串 JavaScript

    给定一个字符串 s,找到 s 中最长的回文子串.你可以假设 s 的最大长度为 1000. 示例 1: 输入: "babad" 输出: "bab" 注意: &qu ...

  10. 在Win7虚拟机下搭建Hadoop2.6.0伪分布式环境

    近几年大数据越来越火热.由于工作需要以及个人兴趣,最近开始学习大数据相关技术.学习过程中的一些经验教训希望能通过博文沉淀下来,与网友分享讨论,作为个人备忘. 第一篇,在win7虚拟机下搭建hadoop ...