Threadlocal 简介及使用

一、threadlocal介绍

        java中的 ThreadLocal类为每一个使用该变量的线程都提供一个变量值的副本,每个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。

二、threadlocal的实现原理

     ThreadLocal是如何做到为每一个线程维护变量副本的呢?在threadlocal类中有个容器用于存储每一个线程的变量的副本。在线程是活动的并且threadLocal对象是可访问的时,该线程就持有一个到该线程局部变量副本的隐含引用,当该线程运行结束后,该线程拥有的所有线程局部变量的副本都将失效,并等待垃圾收集器回收。

三、Threadlocal的常用方法。

       threadLocal类的接口只有四个方法:

      set()      //设置当前线程的线程局部变量的值

      get()            //返回当前线程所对应的线程局部变量

      remove()   //删除当前线程局部变量的值 当对应的线程消失后,容器中对应的线程值并不会被回收,从而造成内存泄露,所以,要进行一下remove()

      protected objectinitialValue()     //返回该线程局部变量的初始值.该方法是一个protected的方法,是为了让子类覆盖而设计的。这是一个延迟调用的方法,在线程第一次调用get() set()时才执行,并且仅执行一次。

四、threadlocal的使用

           使用threadlocal,主要是为了保存一些线程级别的全局变量,比如connection,或者事务上下文,避免这些值需要一直通过函数参数的方式一路传递。

     下面举例用threadlocal封装一个线程级别的全局变量connection,以及在业务中对connection的使用。

    第一步、用threadlocal封装connection

/**
 * 采用ThreadLocal封装Connection
 * 
 * @author liu
 *
 */
public class ConnectionManager {
	
	//定义ThreadLocal容器
	private static ThreadLocal <Connection> connectionHolder = new ThreadLocal<Connection>();
  
	/**
    * 得到connection
    * @return
    */
	public static Connection getConnection(){
		//得到connection
		Connection conn = connectionHolder.get();      //get()方法得到当前线程所对应的线程局部变量
		//如果再当前线程中没有绑定相应的connection
		if(conn == null){
			try {
				JdbcConfig jdbcConfig = XmlConfigReader.getInstance().getJdbcConfig();
				Class.forName(jdbcConfig.getDriverName());
				conn = DriverManager.getConnection(jdbcConfig.getUrl(),jdbcConfig.getUserName(),jdbcConfig.getPassword());
				//将connection设置到Threadlocal
				
				connectionHolder.set(conn);         //set()方法,设置当前线程的线程局部变量的值
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
				throw new ApplicationException("系统错误,请联系管理员!");
			} catch (SQLException e) {
				e.printStackTrace();
				throw new ApplicationException("系统错误,请联系管理员!");
			}
		}
		
		return conn;
	}
	
	/**
	 * 关闭connection
	 */
	public static void close(Connection conn){
		conn = connectionHolder.get();
		if(conn != null){
			try {
				conn.close();
				//将connection从threadLocal集合中清理掉(如果不清楚掉,下次拿到的还是被关闭的这个connection)
				connectionHolder.remove();     //remove()删除当前线程局部变量的值
			} catch (SQLException e) {
				e.printStackTrace();
			}
		
		}
	}

第二步、在业务逻辑层调用到层方法时,不用将connection作为参数进行传递,只需要在最后所有方法调用完之后再关闭connection并清除。

public void addflowCard(FlowCard flowCard) throws ApplicationException {
		
		Connection conn = null;
		try{
			//取得connection
			conn = ConnectionManager.getConnection();
			
			//开始事务
			ConnectionManager.beginTransaction(conn);
			
		  //生成流向单单号
		  String flowCardVouNo = flowCardDao.generateVouNo();
		  //添加流向单主信息
		  flowCardDao.addFlowCardMaster(flowCardVouNo, flowCard);
		  //添加流向单明细信息
		  flowCardDao.addFlowCardDetail(flowCardVouNo, flowCard.getFlowCardDetailList());
		  
		  //提交事务
		  ConnectionManager.commitTransaction(conn);
		  
	  }catch(DaoException e){
		  //回滚事务
		  ConnectionManager.rollbackTransaction(conn);
		  throw new ApplicationException("添加流向单失败!");
	  }finally{
		  //关闭connection并从ThreadLocal中清除
		  ConnectionManager.close(conn);
	  }
	}

第三步、下面是业务逻辑层调用的dao层的两个方法
		public void addFlowCardDetail(String flowCardVouNo,
				List<FlowCardDetail> flowCardDetailList) throws DaoException {
			StringBuffer sbSql =new StringBuffer();
			//sql语句………………
			PreparedStatement pstmt = null;
			try{
				//第一个dao方法中取得connection保存起来,在以后的dao方法中用到的还是此connection
				Connection conn = ConnectionManager.getConnection();
				pstmt =  conn.prepareStatement(sbSql.toString());
				//……给参数赋值……
				//执行sql语句
				pstmt.executeBatch();
			}catch(SQLException e){
				e.printStackTrace();
				throw new DaoException(e);
			}finally{
				//关闭pstmt,connection的关闭由业务逻辑层负责
				ConnectionManager.close(pstmt);
			}
			

		}


		public void addFlowCardMaster(String flowCardVouNo, FlowCard flowCard)
				throws DaoException {
			
			StringBuffer sbSql = new StringBuffer();
			//sql语句………………
			PreparedStatement pstmt = null;
			try{
				Connection conn =  ConnectionManager.getConnection();
				pstmt = conn.prepareStatement(sbSql.toString()); 
				//……给参数赋值……
				//执行sql语句
				pstmt.executeUpdate();
			}catch(SQLException e){
				e.printStackTrace();
				throw new DaoException(e);
			}finally{
				ConnectionManager.close(pstmt);
			}
		}

        上面的代码用通俗的语言描述就是:要保证事务,首先要保证使用的是同一个connection,在一个业务逻辑方法中调用多个dao方法,让这些dao方法使用同一个connection来保证对数据库的操作在一个事务中。在第一个dao方法中取到connection,保存起来,以后dao的方法用到的connection,只能取到此connection,执行完最后一个dao方法,再关闭connection并清除。


    


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值