JAVA学习篇--ThreadLocal,Java中特殊的线程绑定机制


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

 

是什么:


对于ThreadLocal看英文单词我们非常easy理解为一个线程的本地实现。可是它并非一个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层获取这个数据库连接


代码演示样例:


/**
 * 使用threadLocal
 * @author hejingyuan
 *
 */
public class ConnectionManager {

	//private static ThreadLocal<Connection> connectionHolder=new ThreadLocal<Connection>();
	private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>();
	/**
	 * 得到Connection
	 */
	public static Connection getConnection(){
	         //get() 返回此线程局部变量的当前线程副本中的值,假设这是线程第一次调用该方法,则创建并初始化此副本。
		Connection conn=connectionHolder.get();
		//假设在当前线程中没有绑定对应的connection
		if(conn == null){		
			try {
				JdbcConfig jdbcConfig = XmlConfigReader.getInstance().getJdbcConfig();
				Class.forName(jdbcConfig.getDriverName());
				conn = DriverManager.getConnection(jdbcConfig.getUrl(), jdbcConfig.getUserName(), jdbcConfig.getPassword());
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
				throw new ApplicationException("系统错误,请联系系统管理员");
			} catch (SQLException e) {
				e.printStackTrace();
				throw new ApplicationException("系统错误,请联系系统管理员");
			}
		}	
		return conn;
	}
	
	public static void closeConnection(){
		Connection conn=connectionHolder.get();
		if(conn !=null){
			try{
				conn.close();
				//从ThreadLocal中清除Connection
				connectionHolder.remove();
			}catch(SQLException e){
				e.printStackTrace();
			}
		}
	}
	
}

业务逻辑层:

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());
			//flowCardDao.addFlowCardDetail(flowCardVouNo, flowCard.getFlowCardDetailList());
			//提交事务
			ConnectionManager.commitTransaction(conn);
			
		}catch(DaoException e){
			//回滚事务
			ConnectionManager.rollbackTransaction(conn);
			throw new ApplicationException("加入流向单失败");
		}finally{
			//关闭Connection并从threadLocal中清除
			ConnectionManager.closeConnection();
		}
	}

总结

 

  当然ThreadLocal并不能替代同步机制。两者面向的问题领域不同。

同步机制是为了同步多个线程对同样资源的并发訪问。是为了多个线程之间进行通信的有效方式;而ThreadLocal是隔离多个线程的数据共享。从根本上就不在多个线程之间共享资源(变量)。这样当然不须要对多个线程进行同步了。所以,假设你须要进行多个线程之间进行通信,则使用同步机制;假设须要隔离多个线程之间的共享冲突。能够使用ThreadLocal。这将极大地简化你的程序。使程序更加易读、简洁。

 

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

    

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

    同步就是一个公司仅仅有一辆车,员工甲使用的时候其它人仅仅能等待,仅仅有员工甲用完后,其它人才干够使用

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

 

 


posted @ 2017-04-19 17:53  jzdwajue  阅读(264)  评论(0编辑  收藏  举报