在DRP项目中,我们使用了ThreadLocal来创建Connection连接,避免了一直以参数的形式将Connection向下传递(传递connection的目的是由于jdbc事务要求确保使用同一个connection连接)。那么ThreadLocal是如果做到的呢?它和同步锁的不同在哪里?

是什么:

对于ThreadLocal看英文单词我们很容易理解为一个线程的本地实现,但是它并不是一个Thread,而是threadlocalvariable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是Java中一种较为特殊的线程绑定机制,使每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。

解决什么问题:

ThreadLocal是解决线程安全问题一个很好的思路,ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本,由于Key值不可重复,每一个“线程对象”对应线程的“变量副本”,而到达了线程安全。

通过ThreadLocal存取的数据,总是与当前线程相关,也就是说,JVM为每个运行的线程,绑定了私有的本地实例存取空间,从而为多线程环境常出现的并发访问题提供了一种隔离机制。

使用ThreadLocal可以使对象达到线程隔离的目的。同一个ThreadLocal操作不同的Thread,实质是各个Thread对自己的变量操作。

ThreadLocal与其它同步机制的比较:

相同点:

ThreadLocal和其它所有的同步机制都是为了解决多线程中的对同一变量的访问冲突。

不同点:

在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。

举例说明:

对于教师判分来说,每个教师登陆后会从答题记录表中抽取20道学生的答案进行阅卷,为了避免同一道题被多个教师抽到,需要加锁进行控制,保证当时只有一个教师在抽题,其他教师只能等待,只有当这名教师抽题完成,等待的教师才可以进行抽题。同步机制就是为了同步多个线程对相同资源的并发访问,解决了多个线程之间进行通信的问题。

ThreadLocal就从另一个角度来解决多线程的并发访问,ThreadLocal会为每一个线程维护一个和该线程绑定的变量的副本,从而隔离了多个线程的数据,每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。最明显的,ThreadLoacl变量的活动范围为某线程,并且我的理解是该线程“专有的,独自霸占”,对该变量的所有操作均有该线程完成!ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的整个变量封装进ThreadLocal,或者把该对象的特定于线程的状态封装进ThreadLocal。

举例说明:

我们现在软件经常会分层,比如MVC,类似这种横向分层,而ThreadLocal会提供一种方式,方便的在同一个线程范围内,提供一个存储空间,供我们使用,实现纵向的存储结构,便于我们在同一个线程范围内,随时取得我们在另外一个层面存放的数据。

比如:在业务逻辑层需要调用多个Dao层的方法,我们要保证事务(jdbc事务)就要确保他们使用的是同一个数据库连接.那么如何确保使用同一个数据库连接呢?

第一种方案,从业务层创建数据库连接,然后一直将连接以参数形式传递到Dao

第二种方案,使用ThreadLocal,每一个线程拥有自己的变量副本,从业务逻辑层创建connection,然后到Dao层获取这个数据库连接

代码示例:

  1. /**
  2. * 使用threadLocal
  3. * @author hejingyuan
  4. *
  5. */
  6. public class ConnectionManager {
  7. //private static ThreadLocal<Connection> connectionHolder=new ThreadLocal<Connection>();
  8. private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>();
  9. /**
  10. * 得到Connection
  11. */
  12. public static Connection getConnection(){
  13. //get() 返回此线程局部变量的当前线程副本中的值,如果这是线程第一次调用该方法,则创建并初始化此副本。
  14. Connection conn=connectionHolder.get();
  15. //如果在当前线程中没有绑定相应的connection
  16. if(conn == null){
  17. try {
  18. JdbcConfig jdbcConfig = XmlConfigReader.getInstance().getJdbcConfig();
  19. Class.forName(jdbcConfig.getDriverName());
  20. conn = DriverManager.getConnection(jdbcConfig.getUrl(), jdbcConfig.getUserName(), jdbcConfig.getPassword());
  21. } catch (ClassNotFoundException e) {
  22. e.printStackTrace();
  23. throw new ApplicationException("系统错误,请联系系统管理员");
  24. } catch (SQLException e) {
  25. e.printStackTrace();
  26. throw new ApplicationException("系统错误,请联系系统管理员");
  27. }
  28. }
  29. return conn;
  30. }
  31. public static void closeConnection(){
  32. Connection conn=connectionHolder.get();
  33. if(conn !=null){
  34. try{
  35. conn.close();
  36. //从ThreadLocal中清除Connection
  37. connectionHolder.remove();
  38. }catch(SQLException e){
  39. e.printStackTrace();
  40. }
  41. }
  42. }
  43. }

业务逻辑层:

  1. public void addFlowCard(FlowCard flowCard) throws ApplicationException {
  2. Connection conn=null;
  3. try{
  4. //取得Connection
  5. conn=ConnectionManager.getConnection();
  6. //开始事务
  7. ConnectionManager.beginTransaction(conn);
  8. //生成流向单单号
  9. String flowCardVouNo=flowCardDao.generateVouNo();
  10. //添加流向单主信息
  11. flowCardDao.addFlowCardMaster(flowCardVouNo, flowCard);
  12. //添加流向单明细信息
  13. flowCardDao.addFlowCardDetail(flowCardVouNo, flowCard.getFlowCardDetailList());
  14. //flowCardDao.addFlowCardDetail(flowCardVouNo, flowCard.getFlowCardDetailList());
  15. //提交事务
  16. ConnectionManager.commitTransaction(conn);
  17. }catch(DaoException e){
  18. //回滚事务
  19. ConnectionManager.rollbackTransaction(conn);
  20. throw new ApplicationException("添加流向单失败");
  21. }finally{
  22. //关闭Connection并从threadLocal中清除
  23. ConnectionManager.closeConnection();
  24. }
  25. }

总结

  当然ThreadLocal并不能替代同步机制,两者面向的问题领域不同。同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信的有效方式;而ThreadLocal是隔离多个线程的数据共享,从根本上就不在多个线程之间共享资源(变量),这样当然不需要对多个线程进行同步了。所以,如果你需要进行多个线程之间进行通信,则使用同步机制;如果需要隔离多个线程之间的共享冲突,可以使用ThreadLocal,这将极大地简化你的程序,使程序更加易读、简洁。

概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

联系生活中的实例:员工用车

同步就是一个公司只有一辆车,员工甲使用的时候其他人只能等待,只有员工甲用完后,其他人才可以使用

ThreadLocal就是公司为每一个员工配一辆车,每个员工使用自己的车,员工之间用车互不影响,互相不受制约!

Java-ThreadLocal,Java中特殊的线程绑定机制的更多相关文章

  1. ThreadLocal,Java中特殊的线程绑定机制

    在DRP项目中,我们使用了ThreadLocal来创建Connection连接,避免了一直以参数的形式将Connection向下传递(传递connection的目的是由于jdbc事务要求确保使用同一个 ...

  2. Java分享笔记:创建多线程 & 线程同步机制

    [1] 创建多线程的两种方式 1.1 通过继承Thread类创建多线程 1.定义Thread类的子类,重写run()方法,在run()方法体中编写子线程要执行的功能. 2.创建子线程的实例对象,相当于 ...

  3. MVC中Model元数据及绑定机制

    ASP.NET MVC的Model为View Model,表示最终呈现在View上的数据,而Model元数据的一个重要的作用在于控制对象在View上的呈现方式.说得更加具体点,就是基于某种数据类型的M ...

  4. 深入学习AngularJS中数据的双向绑定机制

    来自:http://www.jb51.net/article/80454.htm Angular JS (Angular.JS) 是一组用来开发Web页面的框架.模板以及数据绑定和丰富UI组件.它支持 ...

  5. ThreadLocal(线程绑定)

    为保证在DAO层里的操作都在同一事务里,我们曾使用以参数的形式将Connection向下传递的方式,而ThreadLocal来创建Connection连接,避免了一直以参数的形式将Connection ...

  6. Java并发编程(七)线程封闭

    当访问共享的可变数据时,通常需要使用同步.一种避免使用同步的方式就是不共享数据. 如果仅在单线程内访问数据,就不需要同步.这种技术被称为线程封闭(Thread Confinement),它是实现线程安 ...

  7. 【Java并发基础】局部变量是线程安全的

    前言 方法中的变量(即局部变量)是不存在数据竞争(Data Race)的,也是线程安全的.为了理解为什么,我们先来了一下方法是如何被执行的,然后再分析局部变量的安全性,最后再介绍利用局部变量不会共享的 ...

  8. Java并发包中Lock的实现原理

    1. Lock 的简介及使用 Lock是java 1.5中引入的线程同步工具,它主要用于多线程下共享资源的控制.本质上Lock仅仅是一个接口(位于源码包中的java\util\concurrent\l ...

  9. (第三章)Java内存模型(中)

    一.volatile的内存语义 1.1 volatile的特性 理解volatile特性的一个好办法是把对volatile变量的单个读/写,看成是使用同一个锁对这些单个读/写操作做了同步.下面通过具体 ...

随机推荐

  1. pythonl练习笔记——threading创建进程锁Lock()

    1 基本概述 采用threading.Lock()对象创建锁lock,即 lock = threading.Lock() 其相关方法主要有 lock.acquire() # lock the lock ...

  2. pythonl练习笔记——python线程的GIL

    python线程中的全局解释器锁GIL(Global Interpreter Lock) python-->支持多线程-->同步和互斥-->加锁-->解释器加锁-->解释 ...

  3. Unix环境高级编程(十九)终端I/O

    终端I/O应用很广泛,用于终端.计算机之间的直接连线.调制解调器以及打印机等等.终端I/O有两种不同的工作模式: (1)规范模式输入处理:终端输入以行为单位进行处理,对于每个读要求,终端驱动程序最多返 ...

  4. bzoj 1860: [Zjoi2006]Mahjong麻将 题解

    [原题] 1860: [Zjoi2006]Mahjong麻将 Time Limit: 1 Sec  Memory Limit: 64 MB Submit: 211  Solved: 122 [Subm ...

  5. Django ORM 数据库操作

    比较有用 转自 http://blog.csdn.net/fgf00/article/details/53678205 一.DjangoORM 创建基本类型及生成数据库表结构 1.简介 2.创建数据库 ...

  6. MATLAB(3)——GUI界面设计入门

    作者:桂. 时间:2017-03-01  18:43:35 链接:http://www.cnblogs.com/xingshansi/articles/6485688.html 声明:转载请注明出处, ...

  7. Hadoop本地库介绍及相关问题解决方法汇总

    1.hadoop本地库的作用是什么?2.哪两个压缩编码器必须使用hadoop本地库才能运行?3.hadoop的使用方法?4.hadoop本地库与系统版本不一致会引起什么错误?5.$ export HA ...

  8. t-sql的一些经验

    1.存储过程的3种传回值: 1.以return传回整数 2.以output格式传回参数 3.recordset 2.字符串类型的变量需要初始化后再使用,不然永远是空 DECLARE @FieldsSq ...

  9. Hg(Mercurial)版本管理学习

    1.关闭分支,首先切到你要关闭的分支 hg commit --close-branch -m. 2.仓库ip地址改变之后,重设仓库ip 找到.hg文件夹 - hgrc文件 - 记事本打开重设 3.推分 ...

  10. Oracle PLSQL Demo - 31.执行动态SQL拿一个返回值

    DECLARE v_sql ) := ''; v_count NUMBER; BEGIN v_sql := v_sql || 'select count(1) from scott.emp t'; E ...