另一篇博文:Hibernet中的ThreadLocal使用 http://www.cnblogs.com/gnivor/p/4440776.html

本文参考:
http://blog.csdn.net/lufeng20/article/details/24314381
http://www.cnblogs.com/chenying99/articles/3405161.html

ThreadLocal类接口很简单,只有4个方法,我们先来了解一下:

void set(Object value) 设置当前线程的线程局部变量的值。 
public Object get() 该方法返回当前线程所对应的线程局部变量。 
public void remove() 将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。
注意,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。 
protected Object initialValue() 返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。
这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。

一、知其然

synchronized这类线程同步的机制可以解决多线程并发问题,在这种解决方案下,多个线程访问到的,都是同一份变量的内容。为了防止在多线程访问的过程中,可能会出现的并发错误。不得不对多个线程的访问进行同步,这样也就意味着,多个线程必须先后对变量的值进行访问或者修改,这是一种以延长访问时间来换取线程安全性的策略。

而ThreadLocal类为每一个线程都维护了自己独有的变量拷贝。每个线程都拥有了自己独立的一个变量,竞争条件被彻底消除了,那就没有任何必要对这些线程进行同步,它们也能最大限度的由CPU调度,并发执行。并且由于每个线程在访问该变量时,读取和修改的,都是自己独有的那一份变量拷贝,变量被彻底封闭在每个访问的线程中,并发错误出现的可能也完全消除了。对比前一种方案,这是一种以空间来换取线程安全性的策略。

来看一个运用ThreadLocal来实现数据库连接Connection对象线程隔离的例子。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;public class ConnectionManager {    private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {  @Override  protected Connection initialValue() {  Connection conn = null;  try {  conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "username", "password"); } catch (SQLException e) {  e.printStackTrace();  }  return conn;  }  };  public static Connection getConnection() {  return connectionHolder.get();  }  public static void setConnection(Connection conn) {  connectionHolder.set(conn);  }
}  

  通过调用ConnectionManager.getConnection()方法,每个线程获取到的,都是和当前线程绑定的那个Connection对象,第一次获取时,是通过initialValue()方法的返回值来设置值的。通过ConnectionManager.setConnection(Connection conn)方法设置的Connection对象,也只会和当前线程绑定。这样就实现了Connection对象在多个线程中的完全隔离。在Spring容器中管理多线程环境下的Connection对象时,采用的思路和以上代码非常相似。

附:另一个例子

public class TestNum {    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) {        TestNum sn = new TestNum();        //三个线程共享SN 产生序列号ThreadClient t1 = new ThreadClient(sn);ThreadClient t2 = new ThreadClient(sn);ThreadClient t3 = new ThreadClient(sn);t1.start();t2.start();t3.start();}
}class ThreadClient extends Thread{private TestNum sn ;public ThreadClient(TestNum sn){this.sn = sn;}public void run(){for(int i = 0 ; i < 3 ; i++){System.out.println("Thread: "+ Thread.currentThread().getName() + " sn: " + sn.getNextNum());}}
}

View Code

二、知其所以然

那么到底ThreadLocal类是如何实现这种“为每个线程提供不同的变量拷贝”的呢?先来看一下ThreadLocal的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);  }  

在这个方法内部我们看到,首先通过getMap(Thread t)方法获取一个和当前线程相关的ThreadLocalMap,然后将变量的值设置到这个ThreadLocalMap对象中,当然如果获取到的ThreadLocalMap对象为空,就通过createMap方法创建。

  线程隔离的秘密,就在于ThreadLocalMap这个类。ThreadLocalMap是ThreadLocal类的一个静态内部类,它实现了键值对的设置和获取(对比Map对象来理解),每个线程中都有一个独立的ThreadLocalMap副本,它所存储的值,只能被当前线程读取和修改。ThreadLocal类通过操作每一个线程特有的ThreadLocalMap副本,从而实现了变量访问在不同线程中的隔离。因为每个线程的变量都是自己特有的,完全不会有并发错误。还有一点就是,ThreadLocalMap存储的键值对中的键是this对象指向的ThreadLocal对象,而值就是你所设置的对象了。

为了加深理解,我们接着看上面代码中出现的getMapcreateMap方法的实现:

/** * 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);
}  

代码已经说的非常直白,就是获取和设置Thread内的一个叫threadLocals的变量,而这个变量的类型就是ThreadLocalMap,这样进一步验证了上文中的观点:每个线程都有自己独立的ThreadLocalMap对象。打开java.lang.Thread类的源代码,我们能得到更直观的证明:

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

那么接下来再看一下ThreadLocal类中的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();
}  

再来看setInitialValue()方法:

/** * Variant of set() to establish initialValue. Used instead * of set() in case user has overridden the set() method. * * @return the initial value */  private T setInitialValue() {  T value = initialValue();  Thread t = Thread.currentThread();  ThreadLocalMap map = getMap(t);  if (map != null)  map.set(this, value);  else  createMap(t, value);  return value;  }   

这两个方法的代码告诉我们,在获取和当前线程绑定的值时,ThreadLocalMap对象是以this指向的ThreadLocal对象为键进行查找的,这当然和前面set()方法的代码是相呼应的。

进一步地,我们可以创建不同的ThreadLocal实例来实现多个变量在不同线程间的访问隔离,为什么可以这么做?因为不同的ThreadLocal对象作为不同键,当然也可以在线程的ThreadLocalMap对象中设置不同的值了。通过ThreadLocal对象,在多线程中共享一个值和多个值的区别,就像你在一个HashMap对象中存储一个键值对和多个键值对一样,仅此而已。

设置到这些线程中的隔离变量,会不会导致内存泄漏呢?ThreadLocalMap对象保存在Thread对象中,当某个线程终止后,存储在其中的线程隔离的变量,也将作为Thread实例的垃圾被回收掉,所以完全不用担心内存泄漏的问题。在多个线程中隔离的变量,光荣的生,合理的死,真是圆满,不是么?

最后再提一句,ThreadLocal变量的这种隔离策略,也不是任何情况下都能使用的。如果多个线程并发访问的对象实例只允许,也只能创建一个,那就没有别的办法了,老老实实的使用同步机制(synchronized)来访问吧。

http://my.oschina.net/lichhao/blog/111362

转载于:https://www.cnblogs.com/gnivor/p/4904793.html

Java并发编程--理解ThreadLocal相关推荐

  1. Java并发编程基础--ThreadLocal

    Java并发编程基础之ThreadLocal ​ ThreadLocal是一个线程变量,但本质上是一个以ThreadLocal对象为键.任意对象为值的存储结构,这个结构依附在线程上,线程可以根据一个T ...

  2. Java并发编程:ThreadLocal

    Java并发编程:深入剖析ThreadLocal ThreadLocal ThreadLocal之我见 Struts2中的设计模式----ThreadLocal模式 ThreadLocal Threa ...

  3. Java并发编程实战~ThreadLocal

    ThreadLocal 的使用方法 static class SafeDateFormat {// 定义 ThreadLocal 变量static final ThreadLocal<DateF ...

  4. Java 并发编程:ThreadLocal 的使用及其源码实现

    1.ThreadLocal的使用 防止任务在共享资源上产生冲突的一种方式是根除对变量的共享,使用线程的本地存储为使用相同变量的不同线程创建不同的存储. 下面是一个 ThreadLocal 的实例.这里 ...

  5. Java并发编程:线程封闭和ThreadLocal详解

    什么是线程封闭 当访问共享变量时,往往需要加锁来保证数据同步.一种避免使用同步的方式就是不共享数据.如果仅在单线程中访问数据,就不需要同步了.这种技术称为线程封闭.在Java语言中,提供了一些类库和机 ...

  6. [转载]Java并发编程:深入剖析ThreadLocal

                原文地址:http://www.cnblogs.com/dolphin0520/p/3920407.html 想必很多朋友对ThreadLocal并不陌生,今天我们就来一起探讨 ...

  7. 基于JVM原理、JMM模型和CPU缓存模型深入理解Java并发编程

    许多以Java多线程开发为主题的技术书籍,都会把对Java虚拟机和Java内存模型的讲解,作为讲授Java并发编程开发的主要内容,有的还深入到计算机系统的内存.CPU.缓存等予以说明.实际上,在实际的 ...

  8. Java 并发编程解析 | 如何正确理解Java领域中的多线程模型,主要用来解决什么问题?

    苍穹之边,浩瀚之挚,眰恦之美: 悟心悟性,善始善终,惟善惟道! -- 朝槿<朝槿兮年说> 写在开头 我国宋代禅宗大师青原行思在<三重境界>中有这样一句话:" 参禅之初 ...

  9. Java并发编程个人理解

    Java并发编程 Thread Thread的几种状态 NEW:线程刚创建还没start RUNNABLE: 线程正在JVM中执行 BLOCKED: 当前线程正在等待monitor lock Sync ...

最新文章

  1. 《精通Unix下C语言编程与项目实践》目录
  2. 实例分析exec函数
  3. Docker容器固定IP分配
  4. Zookeeper之Leader选举源码分析
  5. android unity hook,[原创]Unity3d安卓游戏DLL动态调式与HOOK基础
  6. c语言在线测试题,C语言测试题
  7. Swift - 05 - 数值型字面量
  8. 《计算机网络 自顶向下方法》答案(第七章)
  9. 什么是 博弈论?博弈论的研究解决了什么问题?
  10. 肯德尔相关系数计算方法
  11. 内存卡格式化了怎么办?能恢复吗?
  12. adjacent_find
  13. 干货《周志华机器学习详细公式推导版》发布,南瓜书pumpkin-book
  14. 初中计算机科目三必背,驾校科目三路考必背要点口诀(教练整理)
  15. flutter 仿微信展开收起两种写法
  16. 微信小程序分享生成海报
  17. 信息流广告 html5,5分钟让你看懂“信息流广告出价”是怎么回事
  18. python怎样按某一列值拆分Excel表格
  19. 16丨数据分析基础篇答疑
  20. 关于思杰桌面虚拟化的一次交流

热门文章

  1. 百米路由器2登陆地址_腾达无线路由器怎么安装,真的不错
  2. C语言中实际参数太多,c – 宏的实际参数太多了?
  3. java实现报表_用存储过程和 JAVA 写报表数据源有什么弊端?
  4. unbuntu 启动任务脚本_Ubuntu下服务启动脚本编写
  5. java警惕自增的陷阱
  6. 单片机c语言 i%3c%3c1,单片机C语言作业及上机习题及答案
  7. 信息安全主动攻击和被动攻击_信息安全中的主动和被动攻击 网络安全
  8. python求素数算法_Python程序最多可计算n个质数(使用不同算法)
  9. 对视频中的特征颜色物体(青色水杯)进行跟踪
  10. fsync与fflush的关系和区别