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


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

     

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


    1、认识一下ThreaLocal


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




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


    2、ThreaLocal封装Connection实例+解析


           以下的代码仅仅是ThreaLocal封装Connection的核心代码,对于多余的内容成功避开就好,而且有一部分代码是“dom4j解析xml文件。连接数据库”的内容,很适合刚開始学习的人,如有须要,请您移驾到此


    package com.bjpowernode.drp.util;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    /**
     * 採用ThreadLocal封装Connection
     * 仅仅要线程是活动的,没有结束,ThreadLocal是可訪问的,就能够訪问本线程的connection
     * 
     * @author liang
     *
     */
    public class ConnectionManager {
    
    	//使用ThreadLocal保存Connection变量
    	private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>();
    	
    	/**
    	 * 连接Connection
    	 * @return
    	 */
    	public static Connection getConnection(){
    		//ThreadLocal取得当前线程的connection
    		Connection conn = connectionHolder.get();
    		//假设ThreadLocal没有绑定对应的Connection。创建一个新的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);
    			} catch (ClassNotFoundException e) {
    				e.printStackTrace();
    				throw new ApplicationException("系统错误,请联系系统管理员");
    			} catch (SQLException e) {
    				e.printStackTrace();
    			throw new ApplicationException("系统错误。请联系系统管理员");
    			}
    		}
    		return conn;									
    		
    	}
    	/**
    	 * 关闭Connection,清除集合中的Connection
    	 */
    	public static void closeConnection(){
    		//ThreadLocal取得当前线程的connection
    		Connection conn = connectionHolder.get();
    		//当前线程的connection不为空时。关闭connection.
    		if(conn != null){
    			try{
    				conn.close();
    				//connection关闭之后,要从ThreadLocal的集合中清除Connection
    				connectionHolder.remove();
    			}catch(SQLException e){
    				e.printStackTrace();
    			}
    
    		}
    	}
    }


          以下的代码给大家演示了:ThreadLocal怎样在同一个线程中能够共享Connection资源。


    package com.bjpowernode.drp.flowcard.manager.impl;
    
    import java.sql.Connection;
    import java.util.Date;
    import com.bjpowernode.drp.flowcard.dao.FlowCardDao;
    import com.bjpowernode.drp.flowcard.domain.FlowCard;
    import com.bjpowernode.drp.flowcard.manager.FlowCardManager;
    import com.bjpowernode.drp.util.ApplicationException;
    import com.bjpowernode.drp.util.BeanFactory;
    import com.bjpowernode.drp.util.ConnectionManager;
    import com.bjpowernode.drp.util.DaoException;
    import com.bjpowernode.drp.util.PageModel;
    
    public class FlowCardManagerImpl implements FlowCardManager {
    
    	
    	private FlowCardDao flowCardDao;
    	//构造函数
    	public FlowCardManagerImpl(){
    		this.flowCardDao = (FlowCardDao) BeanFactory.getInstance().getDaoObject(FlowCardDao.class);
    	}
    	
    	@Override
    	public void addFlowCard(FlowCard flowCard) throws ApplicationException {
    		
    		Connection conn = null;
    		try{
    			//从ThreadLocal中获取线程相应的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.closeConnection(); } } }


    解析:

     

    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同步机制解决线程安全问题更简单,更方便。而且程序拥有更高的并发性。




  • 相关阅读:
    机器学习15卷积神经网络处理手写数字图片
    机器学习12卷积神经网络
    机器学习11贝叶斯处理邮件分类问题------后续
    机器学习11贝叶斯处理邮件分类问题------待更新
    机器学习10贝叶斯
    机器学习9主成分分析
    机器学习7逻辑回归实践
    机器学习8特征选择
    机器学习6逻辑回归算法
    机器学习5线性回归算法
  • 原文地址:https://www.cnblogs.com/lxjshuju/p/6991196.html
Copyright © 2020-2023  润新知