一、基本概念

1、底层结构:

  • 每个Thread线程都维护一个ThreadLocal.ThreadLocaMap类型的属性threadlocals,threadlocals的k位置管理当前线程threadlocal,value位置管理当前线程的副本中,有点像HashMap,可以保存kv键值对,内部只能保存一个kv的键值对,不同的线程对应不同的副本每个线程只能获取自己的副本,因为threadlocals是每个线程独有的;ThreadLocal.ThreadLocaMap是由一个个继承弱引用的Entry组成,保证threadlocal会被即使回收
  • ThreadLocal的set是首先获取到当前线程,拿到当前线程的threadlocals,将当前threadlcoal作为key,将set方法参数作为value存储到ThreadLocal.ThreadLocaMap中
  • ThreadLocal的get是首先获取到当前线程,拿到当前线程的threadlocals,将当前threadlcoal作为key,去threadlocals查找并拿出关联的value值

2 用法:
②set是为当前线程设置副本
③get则是获取当前线程的副本
④remove()用来移除当前线程中变量的副本

3 作用:
②为每个线程关联一个副本,实现线程数据隔离利用这个特征可以用来管理数据库Connection连接;
③实现线程在上下文传递,避免使用参数的多层级传递

public class ThreadLocalTest implements Runnable {public static Map<String,Object> data = new Hashtable<>(); //ThreadLocal底层结构的模拟对象public static ThreadLocal<Object> threadLocal = new ThreadLocal<Object>();//ThreadLocal实际对象private static Random random = new Random();@Overridepublic void run() {//生成一个随机数并将它作为value值,以线程名为key保存搭配map里Integer integer = random.nextInt(1000);String name = Thread.currentThread().getName();//获取当前线程名;System.out.println("当前线程"+name+"生成的随机数是:"+integer );data.put(name,integer);  //模拟ThreadLocal底层添加数据操作threadLocal.set(integer); //ThreadLocal实际添加数据try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}Object o1 = data.get(name); //模拟ThreadLocal底层取出数据操作Object o2 = threadLocal.get(); //ThreadLocal的取出数据操作System.out.println("线程"+name+"结束前取出数值"+o2);//取出new User().create();}public static void main(String[] args) {for (int i = 0; i < 3; i++) {new Thread(new ThreadLocalTest()).start();}}}class User{public void create(){String name = Thread.currentThread().getName();//模拟User类通过ThreadLocal底层取出数据的底层操作
//        System.out.println("在User类中取出线程"+name+"数据为:"+ ThreadLocalTest.data.get(name));//在User类通过ThreadLocal取出数据的操作System.out.println("在User类中取出线程"+name+"数据为:"+ ThreadLocalTest.threadLocal.get());}
}

二、最佳实践

/*
1 要保证所有的操作要么都成功要么都失败? => 使用数据库事物
2 如何保证全部操作都在同一事物内? => 所有的操作都必须使用同一个connection链接对象
3 如何保证所有的操作都必须使用同一个connection链接对象?=> 首先将这些操作通常是位于同一个线程内的
=> 只需要为一个线程绑定一个数据库连接对象就可
4 用ThreadLocal来防止一个线程获取多个数据库连接对象 (因为ThreadLocal是线程独立的),过程参考下面代码*/
class JDBCUtils {//-----------------------------------------------Druid链接池返回链接对象------------------------------------------------private static DataSource dlSource = null;//形式是:默认的隐形key threadName => conn;private static ThreadLocal<Connection> conns = new ThreadLocal<Connection>();//以静态代码块的形式创建dlSource链接池资源static {Properties pros = new Properties();//读取druid配置文件,内容无非是一些数据库链接使用的url,username,password...InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("Druid.properties");try {pros.load(is);} catch (IOException e) {e.printStackTrace();}try {dlSource = DruidDataSourceFactory.createDataSource(pros);} catch (Exception e) {e.printStackTrace();}}public static Connection getDlConnection() {//判断当前线程是否已经关联了一个连接对象(意味着这个线程之前已经获取到链接了)Connection conn = conns.get();//如果是null,意味着当前线程还没有创建过数据库连接对象,此时我们要去连接池中去获取链接然后保存到conns中//否则,就意味着当前线程已有数据库连接对象,直接返回之前创建的就行了if(conn == null){try {conn = dlSource.getConnection();conns.set(conn);//为线程A保存刚刚获取的数据库连接对象connconn.setAutoCommit(false);} catch (SQLException e) {e.printStackTrace();}}return conn;}
}

用法总结

  • 管理数据库连接对象,管理session会话
  • 线程间数据隔离,保证线程安全
  • 事物操作,存储线程信息,spring框架在事物开始时会绑定一个jdbc connection,实现事物的隔离性;保证事物正常执行 => 保证所有操作都在同一连接下=>一个线程只能对应一个数据库连接 => 使用ThreadLocal来完成

ThreadLocal原理和用法相关推荐

  1. ThreadLocal原理以及用法详解

    目录 一.什么是ThreadLocal? 二.ThreadLocal与Synchronized的区别? 三.什么场景用ThreadLocal? 1.Spring源码用到了 2.项目全局添加ip用到了 ...

  2. ThreadLocal原理及用法详解

    背景 一直以来对ThreadLocal用法模棱两可,不知道怎么用今天好好研究了下给大家分享下. 1.讲解ThreadLocal之前先回顾下什么是取模.x^y.弱引用. 1. 取模运算实际上是计算两数相 ...

  3. ThreadLocal原理解析以及是否需要调用remove方法

    平常的开发过程中,如果有个类不是线程安全的,比如SimpleDateFormat,要使这个类在并发的过程中是线程安全的,那么可以将变量设置位局部变量,不过存在的问题就是频繁的创建对象,对性能和资源会有 ...

  4. mysql 实时聚合分析,mysql累积聚合原理与用法实例分析

    本文实例讲述了mysql累积聚合原理与用法.分享给大家供大家参考,具体如下: 累积聚合为聚合从序列内第一个元素到当前元素的数据,如为每个员工返回每月开始到现在累积的订单数量和平均订单数量 行号问题有两 ...

  5. python装饰器原理-Python装饰器原理与用法分析

    这篇文章主要介绍了Python装饰器原理与用法,结合实例形式分析了Python装饰器的概念.原理.使用方法及相关操作注意事项,需要的朋友可以参考下 本文实例讲述了Python装饰器原理与用法.分享给大 ...

  6. python的编程模式-Python设计模式之状态模式原理与用法详解

    本文实例讲述了Python设计模式之状态模式原理与用法.分享给大家供大家参考,具体如下: 状态模式(State Pattern):当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类 ...

  7. python装饰器原理-Python函数装饰器原理与用法详解

    本文实例讲述了Python函数装饰器原理与用法.分享给大家供大家参考,具体如下: 装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值 ...

  8. python装饰器原理-python装饰器原理与用法深入详解

    本文实例讲述了python装饰器原理与用法.分享给大家供大家参考,具体如下: 你会Python嘛? 我会! 那你给我讲下Python装饰器吧! Python装饰器啊?我没用过哎 以上是我一个哥们面试时 ...

  9. python定义私有变量的方法_Python面向对象程序设计之私有变量,私有方法原理与用法分析...

    本文实例讲述了Python面向对象程序设计之私有变量,私有方法原理与用法.分享给大家供大家参考,具体如下: 私有变量,私有方法:python的私有化是为了规划私有属性,避免非相关的访问[假如!我有老婆 ...

最新文章

  1. 表单验证--插件-1.0.1
  2. Python Django 文件下载代码示例
  3. exp oracle 表空间,oracle之EXP导出表空间错误解决
  4. 如何在vue中使用sass
  5. 全志A33-ARM linux开机进入uboot命令行
  6. JS类似Java String.format的函数
  7. 用Proteus学习51单片机之I2C(IIC)总线
  8. 栈——后进先出(LIFO:last in first out)
  9. 个人防骗大全精选(1)
  10. 如何根据函数,绘制出函数的图像
  11. 中国鞋服行业首个AIGC准专业级设计大赛来了!
  12. Unity 接入第三方SDK打包闪退
  13. 客服管理之如何使用座席劵添加子账号(客服人数)
  14. Java学习-发红包案例
  15. SSH git初次克隆代码问题报错 fatal: unable to update url base from redirection:
  16. C++获取数组的长度
  17. C语言计算程序运行时间简单实例
  18. 苹果主题商店_苹果手机怎么换铃声?全网最简单的教程来了,小白秒学会
  19. 挂缀codevs2382
  20. 基础类与基础算法学习

热门文章

  1. Javascript开发的HTML5游戏的知识产权保护
  2. 操作数组的常用方式二-----排序、查找
  3. 辅助类——掌握内容管道
  4. 代码生成工具之MyGeneration
  5. vue-seamless-scroll在小米手机上显示不正常 显示出两行的问题
  6. Batch Normalization批量归一化
  7. 南开大学计算机本科论文,南开大学本科毕业论文设计-南开大学教务处主页.DOC...
  8. java爬虫隐藏表单提交_java爬虫--jsoup简单的表单抓取案例
  9. linux硬盘系统安装教程图解,Linux操作系统添加安装新硬盘的方法图解
  10. vue根据指令动态改变title名字