深入解析ThreadLocal底层实现原理

原创 2018年04月16日 13:06:06

        学习Java中常用的开源框架,Mybatis、Hibernate中设计到线程通过数据库连接对象Connection,对其数据进行操作,都会使用ThreadLocal类来保证Java多线程程序访问和数据库数据的一致性问题。就想深入了解一下ThreadLocal类是怎样确保线程安全的!详解如下:

一、对其ThreadLocal类的大致了解

       ThreadLocal ,也叫线程本地变量,可能很多朋友都知道ThreadLocal为变量在每个线程中都创建了所使用的的变量副本。使用起来都是在线程的本地工作内存中操作,并且提供了set和get方法来访问拷贝过来的变量副本。底层也是封装了ThreadLocalMap集合类来绑定当前线程和变量副本的关系,各个线程独立并且访问安全!

    

public class DBUtil {
	//创建一个存储数据库连接对象的ThreadLocal线程本地变量
	private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();

	static{
		try {
			//注册驱动
			DriverManager.registerDriver(new oracle.jdbc.OracleDriver());
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	/*
	 * 获取数据库的连接对象
	 */
	public static Connection getConnected(){
		Connection conn = null;
		conn = tl.get();		//第一步:从ThreadLocal对象当中去获取
		if(conn == null){		//若没有获取到,原始方法获取
			try {
				conn = DriverManager.getConnection("jdbc:oracle:thin:@192.168.122.1:1521/xe","store","store_password");
				//获取连接对象以后,都设置为默认手动提交
				conn.setAutoCommit(false);	
				//第二部:将连接对象放入对应的ThreadLocal泛型对象tl当中(进而绑定到使用它的线程对象上)
				tl.set(conn);
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return conn;
	}

	/*
	 * 关闭数据库的连接,并删除对应的ThreadLocal中的对象
	 */
	public static void closeConnection(){
		Connection conn = null;
		conn = tl.get();		//第三步:使用完毕,再次获取对象
		if(conn != null){
			tl.remove();		//第四步:线程操作数据库完毕,移除
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}

        上述例子中使用ThreadLocal类来绑定对应线程和Connection之间的关系,确保访问数据库数据的安全性问题;大家想象一下,如果没有使用ThreadLocal类来绑定,那么多个线程同时进入getConnected()方法,有可能获取的是同一个Connection对象,导致线程不安全问题!

二、深入理解ThreadLoca类

    (1)set操作,为线程绑定变量:    

 public void set(T value) {
    Thread t = Thread.currentThread();//1.首先获取当前线程对象
        ThreadLocalMap map = getMap(t);//2.获取该线程对象的ThreadLocalMap
        if (map != null)
            map.set(this, value);//如果map不为空,执行set操作,以当前threadLocal对象为key,实际存储对象为value进行set操作
        else
            createMap(t, value);//如果map为空,则为该线程创建ThreadLocalMap
    }

可以很清楚的看到,ThreadLocal只不过是个入口,真正的变量副本绑定到当前线程上的。

    //Thread中的成员变量
    ThreadLocal.ThreadLocalMap threadLocals = null;		//每个Thread线程中都封装了一个ThreadLocalMap对象
		
    //ThreadLocal类中获取Thread类中的ThreadLocalMap对象
    ThreadLocalMap getMap(Thread t) {
	return t.threadLocals;
    }
		
    //ThreadLocal类中创建Thread类中的ThreadLocalMap成员对象
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

现在,我们可以看出ThreadLocal的设计思想了:

(1) ThreadLocal仅仅是个变量访问的入口;

(2) 每一个Thread对象都有一个ThreadLocalMap对象,这个ThreadLocalMap持有对象的引用;

(3) ThreadLocalMap以当前的threadLocal对象为key,以真正的存储对象为value。get()方法时通过threadLocal实例就可以找到绑定在当前线程上的副本对象。

看上去有点绕。我们完全可以设计成Map<Thread,Value>这种形式,一个线程对应一个存储对象。

ThreadLocal这样设计有两个目的:

        第一:可以保证当前线程结束时,相关对象可以立即被回收;第二:ThreadLocalMap元素会大大减少,因为Map过大容易造成哈希冲突而导致性能降低。

(2)看get()方法

 public T get() {
     Thread t = Thread.currentThread();//1.首先获取当前线程
         ThreadLocalMap map = getMap(t);//2.获取线程的map对象
         if (map != null) {//3.如果map不为空,以threadlocal实例为key获取到对应Entry,然后从Entry中取出对象即可。
             ThreadLocalMap.Entry e = map.getEntry(this);
             if (e != null)
                 return (T)e.value;
         }
         return setInitialValue();//如果map为空,也就是第一次没有调用set直接get(或者调用过set,又调用了remove)时,为其设定初始值
     }
void createMap(Thread t, T firstValue) {	//this指的是ThreadLocal对象
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}
	    
private T setInitialValue() {
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
         map.set(this, value);
    else
        createMap(t, value);
        return value;
}

三、应用场景:

        ThreadLocal对象通常用于房子对可变的单实例变量或全局变量进行共享。例如:由于JDBC的连接对象不是线程安全的,因此,当多个线程应用程序在没有协同的情况下,使用全局变量时,就是线程不安全的。通过将JDBC的连接对象保存到ThreadLocal中,每个线程都会拥有自己的连接对象副本。

        ThreadLocal在Spring的事物管理,包括Hibernate管理等都有出现,在web开发中,有事会用来管理用户回话HttpSession,web交互这种典型的一请求一线程的场景似乎比较适合使用ThreadLocal,但是需要注意的是,由于此时session与线程关联,而Tomcat这些web服务器多采用线程池机制,也就是说线程是可以复用的,所以在每次进入的时候都需要重新进行set操作,或者使用完毕以后及时remove掉!

    
 public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());        //先获取ThreadLocalMap对象实例
         if (m != null)        //直接通过threadLocal实例删除value值
             m.remove(this);
     }




版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/super_YC/article/details/79958414

ThreadLocal的底层实现原理与应用场景

维持线程封闭性的一种规范方法是使用ThreadLocal。它提供了set和get等访问方法,这些方法为每个使用该变量的线程都存有一份独立的副本,因此get方法总是返回由当前执行线程在调用set时设置的...
  • qq_24693837
  • qq_24693837
  • 2017年04月22日 14:14
  • 2482

《深入解析Spring架构与设计原理》阅读笔记1

Ioc容器概念以及形象比喻: 1,我们通常所说的IOC容器 实际上是代表了一系列的功能各异的产品  只是容器的功能大小,功能不同 各有特点。 2,,IOC比喻:就像水桶一样 有金属的 有塑料的 有各种...
  • weixin_36642826
  • weixin_36642826
  • 2016年11月29日 23:31
  • 119

PageHelper 实现原理

PageHelper 原理
  • tengdazhang770960436
  • tengdazhang770960436
  • 2016年08月04日 15:11
  • 4165

Spring技术内幕——深入解析Spring架构与设计原理(一)IOC实现原理

文章来源: javaeye.comhttp://www.javaeye.com/topic/493282?page=1 相关文章:   Spring源代码解析(一):IOC容器 Spring技术内幕—...
  • yangdelong
  • yangdelong
  • 2009年10月26日 11:32
  • 9744

ThreadLocal类详解与源码分析

概述我们知道Spring通过各种DAO模板类降低了开发者使用各种数据持久技术的难度。这些模板类都是线程安全的,也就是说,多个DAO可以复用同一个模板实例而不会发生冲突。 我们使用模板类访问底层数据,根...
  • shenlei19911210
  • shenlei19911210
  • 2015年11月26日 19:48
  • 2702

【源码学习】ThreadLocal 实现原理以及其内部存储结构(神奇的1640531527)

在线程中使用ThreadLocal保存数据可以保证在各个线程之间各自的数据是相互隔离的。 ThreadLocal的实际存储容器是自己实现的一个ThreadLocalMap,其对象保存在线程中,也就是说...
  • lingchen881218
  • lingchen881218
  • 2017年05月31日 20:26
  • 300

Hive进阶-深入解析Hive底层实现 - Distinct 的底层实现

转自Hive – Distinct 的实现 并稍作更改 http://ju.outofmemory.cn/entry/784 Hive版本为1.1.0。有空的话其实可以分析它在hive on...
  • zuolovefu
  • zuolovefu
  • 2018年01月10日 16:40
  • 1478

深入理解java集合的底层操作

集合:层次结构:(1)Collection  (2)Enum
  • u012486727
  • u012486727
  • 2014年04月19日 21:15
  • 2065

深入php内核,从底层c语言剖析php实现原理

深入php内核,从底层c语言讲诉php实现原理 非常好的电子书:http://www.cunmou.com/phpbook/preface.md 这是它的目录: PHP的生命周期 让我们从SA...
  • nuli888
  • nuli888
  • 2016年08月05日 10:53
  • 8553

Mybatis实现原理深入解析

Mybatis实现原理深入解析 转载自http://yuncode.net/article/a_5525ec26eee5268 未知真实作者。。。...
  • danfly1010
  • danfly1010
  • 2016年12月02日 15:22
  • 3308
收藏助手
不良信息举报
您举报文章:深入解析ThreadLocal底层实现原理
举报原因:
原因补充:

(最多只允许输入30个字)