【java项目实战】ThreadLocal封装Connection,实现同一线程共享资源

 线程安全一直是程序猿们关注的焦点,多线程也一直是比较让人头疼的话题,想必大家曾经也遇到过各种各种的问题,我就不再累述了。当然,解决方式也有很多,这篇博文给大家提供一种很好的解决线程安全问题的思路。

 

      首先,我们先简单的认识一下ThreadLocal,之后是实例+解析,最后一句话总结。


1、认识一下ThreaLocal


       认识ThreadLocal必须要通过api文档,不仅仅具有说服力,而且它会给你更加全面的解释。下面我我给大家从api文档上截取一张图,并标出来了七点需要重点理解的内容,实例过后的解析也是重点解释这七部分。




      对于上面的内容,不理解没有关系,我们通过下面的实例加深一下理解,实例之后我会给大家一个更加深入的解释。


2、ThreaLocal封装Connection实例+解析


       下面的代码只是ThreaLocal封装Connection的核心代码,对于多余的内容成功避开就好,并且有一部分代码是“dom4j解析xml文件,连接数据库”的内容,非常适合初学者,如有需要,请您移驾到此


[java]  view plain  copy
 print ?
  1. package com.bjpowernode.drp.util;  
  2.   
  3. import java.sql.Connection;  
  4. import java.sql.DriverManager;  
  5. import java.sql.ResultSet;  
  6. import java.sql.SQLException;  
  7. import java.sql.Statement;  
  8.   
  9. /** 
  10.  * 采用ThreadLocal封装Connection 
  11.  * 只要线程是活动的,没有结束,ThreadLocal是可访问的,就可以访问本线程的connection 
  12.  *  
  13.  * @author liang 
  14.  * 
  15.  */  
  16. public class ConnectionManager {  
  17.   
  18.     //使用ThreadLocal保存Connection变量  
  19.     private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>();  
  20.       
  21.     /** 
  22.      * 连接Connection 
  23.      * @return 
  24.      */  
  25.     public static Connection getConnection(){  
  26.         //ThreadLocal取得当前线程的connection  
  27.         Connection conn = connectionHolder.get();  
  28.         //如果ThreadLocal没有绑定相应的Connection,创建一个新的Connection,  
  29.         //并将其保存到本地线程变量中。  
  30.         if(conn == null){  
  31.             try {  
  32.                 JdbcConfig jdbcConfig = XmlConfigReader.getInstance().getJdbcConfig();  
  33.                 Class.forName(jdbcConfig.getDriverName());                
  34.                 conn = DriverManager.getConnection(jdbcConfig.getUrl(), jdbcConfig.getUserName(), jdbcConfig.getPassword());  
  35.                 //将当前线程的Connection设置到ThreadLocal  
  36.                 connectionHolder.set(conn);  
  37.             } catch (ClassNotFoundException e) {  
  38.                 e.printStackTrace();  
  39.                 throw new ApplicationException("系统错误,请联系系统管理员");  
  40.             } catch (SQLException e) {  
  41.                 e.printStackTrace();  
  42.             throw new ApplicationException("系统错误,请联系系统管理员");  
  43.             }  
  44.         }  
  45.         return conn;                                      
  46.           
  47.     }  
  48.     /** 
  49.      * 关闭Connection,清除集合中的Connection 
  50.      */  
  51.     public static void closeConnection(){  
  52.         //ThreadLocal取得当前线程的connection  
  53.         Connection conn = connectionHolder.get();  
  54.         //当前线程的connection不为空时,关闭connection.  
  55.         if(conn != null){  
  56.             try{  
  57.                 conn.close();  
  58.                 //connection关闭之后,要从ThreadLocal的集合中清除Connection  
  59.                 connectionHolder.remove();  
  60.             }catch(SQLException e){  
  61.                 e.printStackTrace();  
  62.             }  
  63.   
  64.         }  
  65.     }  
  66. }  


      下面的代码给大家演示了:ThreadLocal如何在同一个线程中可以共享Connection资源。


[java]  view plain  copy
 print ?
  1. package com.bjpowernode.drp.flowcard.manager.impl;  
  2.   
  3. import java.sql.Connection;  
  4. import java.util.Date;  
  5. import com.bjpowernode.drp.flowcard.dao.FlowCardDao;  
  6. import com.bjpowernode.drp.flowcard.domain.FlowCard;  
  7. import com.bjpowernode.drp.flowcard.manager.FlowCardManager;  
  8. import com.bjpowernode.drp.util.ApplicationException;  
  9. import com.bjpowernode.drp.util.BeanFactory;  
  10. import com.bjpowernode.drp.util.ConnectionManager;  
  11. import com.bjpowernode.drp.util.DaoException;  
  12. import com.bjpowernode.drp.util.PageModel;  
  13.   
  14. public class FlowCardManagerImpl implements FlowCardManager {  
  15.   
  16.       
  17.     private FlowCardDao flowCardDao;  
  18.     //构造函数  
  19.     public FlowCardManagerImpl(){  
  20.         this.flowCardDao = (FlowCardDao) BeanFactory.getInstance().getDaoObject(FlowCardDao.class);  
  21.     }  
  22.       
  23.     @Override  
  24.     public void addFlowCard(FlowCard flowCard) throws ApplicationException {  
  25.           
  26.         Connection conn = null;  
  27.         try{  
  28.             //从ThreadLocal中获取线程对应的Connection  
  29.             conn = ConnectionManager.getConnection();  
  30.             //开始事务  
  31.             ConnectionManager.beginTransaction(conn);  
  32.             //生成流向单单号  
  33.             String flowCardVouNo = flowCardDao.generateVouNo();  
  34.             //添加流向单主信息  
  35.             flowCardDao.addFlowCardMaster(flowCardVouNo, flowCard);  
  36.             //添加流向单明细信息  
  37.             flowCardDao.addFlowCardDetail(flowCardVouNo, flowCard.getFlowCardDetailList());  
  38.             //提交事务  
  39.             ConnectionManager.commitTransaction(conn);        
  40.         }catch(DaoException e){  
  41.             //回滚事务  
  42.             ConnectionManager.rollbackTransaction(conn);  
  43.             throw new ApplicationException("添加流向单失败!");  
  44.         }finally{  
  45.             //关闭Connection并从ThreadLocal集合中清除  
  46.             ConnectionManager.closeConnection();  
  47.         }  
  48.       
  49.     }  
  50. }  

解析:

 

1、该类提供了线程局部变量,它独立于变量的初始化副本

 

       大家可能对局部变量不太理解,为什么不是成员变量或全局变量,此时就涉及到变量的作用域问题。ThreadLocal具有比局部变量更大一点的作用域,在此作用域内资源可以共享,线程是安全的。

       我们还了解到ThreadLocal并不是本地线程,而是一个线程变量,它只是用来维护本地变量。针对每个线程提供自己的变量版本,避免了多线程的冲突问题,每个线程只需要维护自己的版本就好,彼此独立,不会影响到对方。

 

2、每个线程有自己的一个ThreadLocal,修改它并不影响其他线程

  

      我们根据下面这张图可以看到,向ThreadLocal里面存东西就是创建了一个Map,一个线程对应一个Map集合,然后ThreadLocal把这个Map挂到当前的线程底下,一个key值对应一个value,这样Map就只属于当前线程。(如果您不理解Map的特点可以猛戳




3、在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。 

 

      上面我们知道了变量副本的存放在了map中,当我们不在调用set,此时不在将引用指向该‘map’,而本线程退出时会执行资源回收操作,将申请的资源进行回收,其实就是将引用设置为null。这时已经不在有任何引用指向该map,故而会被垃圾回收。


3、对比ThreadLocal和synchronized同步机制


相同点:

        1、ThreadLocal和线程同步机制都能解决多线程中相同变量的访问冲突问题。

不同点:

       1、适用的情况不同

 

        在同步机制中,使用同步保证同一时间只有一个线程访问,不能同时访问共享资源,否则就是出现错误。ThreadLocal则隔离了相关的资源,并在同一个线程中可以共享这个资源。彼此独立,修改不会影响到对方。

 

       2、最终实现的效果不同

    

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

 

      上面博客的链接同样也是线程同步机制synchronized的实例,大家可以通过两个实例体会一下它们的异同点,再加上异同点解析,相信您对它们已经有了很深刻的认识。


4、一句话总结ThreadLocal


       ThreadLocal是解决线程安全问题一个很好的思路,在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,并且程序拥有更高的并发性。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值