理解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代码  

  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源码)

Java代码  

  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型:

Java代码  

  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()里什么操作都没有,唯一的操作是这句:

Java代码  

  1. private final int threadLocalHashCode = nextHashCode();

那么nextHashCode()做了什么呢:

Java代码  

  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()方法,两句合并一下成为

Java代码  

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

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

Java代码  

  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. }

再看这句:

Java代码  

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

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

理解ThreadLocal(二)

时间: 2024-08-27 03:18:48

理解ThreadLocal(二)的相关文章

理解ThreadLocal背后的概念

介绍 我之前在任何场合都没有使用过thread local,因此没有注意到它,直到最近用到它的时候. 前提信息 线程可以理解为一个单独的进程,它有自己的调用栈.在java中每一个线程都有一个调用栈或者说每一个调用栈都有一个线程,即使你不在你的程序中创建线程,线程仍然会在你不知道的情况下运行.最简单的例子就是,当你通过main方法启动一个简单的java程序时,你不在程序里调用new Thread().start(),但是JVM仍然会创建一个main thread 去运行main方法. main线程

【Java】深入理解ThreadLocal

一.前言 要理解ThreadLocal,首先必须理解线程安全.线程可以看做是一个具有一定独立功能的处理过程,它是比进程更细度的单位.当程序以单线程运行的时候,我们不需要考虑线程安全.然而当一个进程中包含多个线程的时候,就需要考虑线程安全问题,因为此时线程可能会同时操作同一个资源,当两个或者两个以上线程同时操作一个资源的时候,就会造成冲突.不一致等问题,即线程不安全. 解决线程安全问题,本质上就是解决资源共享问题,一般有以下手段: 1)可重入(不依赖环境):2)互斥(同一时间段只允许一个线程使用)

Java 并发:深入理解 ThreadLocal

摘要: ThreadLocal 又名线程局部变量,是 Java 中一种较为特殊的线程绑定机制,用于保证变量在不同线程间的隔离性,以方便每个线程处理自己的状态.进一步地,本文以ThreadLocal类的源码为切入点,深入分析了ThreadLocal类的作用原理,并给出应用场景和一般使用步骤. 一. 对 ThreadLocal 的理解 1). ThreadLocal 概述 ThreadLocal 又名 线程局部变量,是 Java 中一种较为特殊的 线程绑定机制,可以为每一个使用该变量的线程都提供一个

一个例子理解threadLocal用法

ThreadLocal可以使对象达到线程隔离的目的.话不多说直接上代码: /** * 一个例子理解threadLocal * * 一个单例模式的类 */ public class SingleThreadLocalTest { private static SingleThreadLocalTest single = new SingleThreadLocalTest(); private ThreadLocal<String> threadLocal = new ThreadLocal<

深入理解OOP(二):多态和继承(继承)

本文是深入浅出OOP第二篇,主要说说继承的话题. 深入理解OOP(一):多态和继承(初期绑定和编译时多态) 深入理解OOP(二):多态和继承(继承) 深入理解OOP(三):多态和继承(动态绑定和运行时多态) 深入理解OOP(四):多态和继承(C#中的抽象类) 深入理解OOP(五):C#中的访问修饰符(Public/Private/Protected/Internal/Sealed/Constants/Static and Readonly Fields) 深入理解OOP(六):枚举(实用方法)

设计模式理解(二)创建型——单例、原型

设计模式理解(二)单例(Singleton)与原型(Prototype) 为什么一起写,因为懒.... 单例,就是用了面向对象语言的一些奇技淫巧,把构造函数私有了,然后用一个自身类型的静态指针作为全局唯一的实例的引用.碰到并发之类就呵呵了. 这么简单都能画个UML图,还煞有介事似的,我晕. -----------------------   分割线   ------------------------ 原型(Prototype) 我理解就是给一个类糊弄个cloneable之类的接口,让它自己决定

Android学习笔记十四.深入理解fragment(二) 之《图书详情》实战

深入理解fragment(二) 之<图书详情>实战 通过上一篇博文<深入理解fragment一>,我们学习了Android-Fragment的核心知识点.现在在此基础上,利用Fragment技术开发一款适用于大屏幕手机/平板的查找图书详情的应用软件.该项目主要在于两方面,一是Activity.Fragment的源码实现:二是,布局界面资源文件的实现. 1.res/../BookListFragment.java: 自定义类,继承于ListFragment,无需实现OnCreateV

理解ThreadLocal类

1 ThreadLocal是什么 早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路.使用这个工具类可以很简洁地编写出优美的多线程程序. ThreadLocal,顾名思义,它不是一个线程,而是线程的一个本地化对象.当工作于多线程中的对象使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程分配一个独立的变量副本.所以每一个线程都可以独立地改变自己的副本,而不会影响其他线程所对应

[JavaEE]理解ThreadLocal

转http://www.iteye.com/topic/103804 首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的.各个线程中访问的是不同的对象. 另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象,每个线程创建一个,不是什