面试中我们经常会问到多线程,说到多线程,基本都会被问到多线程的内变量的安全性,多线程的安全性我们使用-ThreadLocal
ThreadLocal原理:
1、多个线程访问同一共享变量时,ThreadLocal类为每个线程提供一份该变量的副本
2、各个线程拥有一份属于自己的变量副本,操作修改的是各自的变量副本,而不会相互影响
3、线程与线程之间,更不会有干扰,每个线程访问的都是自己的ThreadLocalMap
我们使用多线程还会遇到:Spring的并发问题——有状态Bean和无状态Bean
有状态会话bean :
每个用户有自己特有的一个实例,在用户的生存期内,bean保持了用户的信息,即“有状态”;一旦用户灭亡(调用结束或实例结束),bean的生命期也告结束。即每个用户最初都会得到一个初始的bean。简单来说,有状态就是有数据存储功能。有状态对象(Stateful Bean),就是有实例变量的对象 ,可以保存数据,是非线程安全的。
无状态会话bean :
bean一旦实例化就被加进会话池中,各个用户都可以共用。即使用户已经消亡,bean 的生命期也不一定结束,它可能依然存在于会话池中,供其他用户调用。由于没有特定的用户,那么也就不能保持某一用户的状态,所以叫无状态bean。但无状态会话bean 并非没有状态,如果它有自己的属性(变量),那么这些变量就会受到所有调用它的用户的影响,这是在实际应用中必须注意的。简单来说,无状态就是一次操作,不能保存数据。无状态对象(Stateless Bean),就是没有实例变量的对象 .不能保存数据,是不变类,是线程安全的。
Spring使用ThreadLocal解决线程安全问题:
1、通常情况下,只有无状态的Bean才可以在多线程环境下共享 2、Spring对一些有状态Bean中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态 3、eg: TransactionSynchronizationManager、 RequestContextHolder、 AopContext 、LocaleContextHolder等等
ThreadLocal在分层架构中的应用:
1、在多层架构中,从接收请求到返回响应所经过的所有程序调用都同属于一个线程 2、在多层架构中,下层通过接口向上层开放功能调用 3、在多层架构中,将一些非线程安全的变量以ThreadLocal存放 4、在同一次请求响应的调用线程中,所有对象所访问的同一ThreadLocal变量都是当前线程所绑定的
ThreadLocal经常会应用在数据库连接:
原始数据库连接:
1、jdbc连接数据库-问题:线程不安全,一个connection对应的是一个事物,一次、2、2、2、Drivermanager.getConnection(jdbcurl)获得只是一个connection,并不能满足高并发情况;
3、数据库连接优化:
使用数据库连接池,减少线程创建、销毁时间,提高性能
使用threadlocal,保证同一个事务,同一个数据库连接的一致性
pool.getConnection(),都是先从threadlocal获取,如果threadlocal有 值,则用,保证线程里的多个dao操作,用的是 同一个数据库连接,以保证事务;如果没有,则将新的connection放在threadlocal里,再get给到线程
ThreadLocal在Struts2中的应用:
1、ActionContext是Action的上下文,它保存了在Action执行过程中所需的request,session, parameters, locale等对象
2、Struts2会根据每个执行HTTP请求的线程来创建对应的ActionContext,即一个线程有一个唯 一的ActionContext
3、ActionContext是线程安全的,基于ThreadLocal 实现,参考ActionContext源码
ThreadLocal在分布式服务框架中的应用:
1、分布式服务框架对缓存、数据库、消息队列操作进行封装埋点,实现透明的实现服务调用 跟踪
实现方法:
1、在App请求的入口生成唯一requestID,放入ThreadLocal 2、缓存访问层代码埋点,从ThradLocal取出requestID,记录缓存操作耗时 3、数据库访问层代码埋点,从ThradLocal取出requestID,记录数据库操作耗时 4、调用其它微服务 (RPC),将requestID传递到下一个微服务,微服务将接收到的requestID存入ThreadLocal 5、微服务内部的缓存、数据库操作同样记录requestID操作耗时及错误信息 6、消息队列写入、消费代码埋点,传递requestID,记录消息消费耗时
ThreadLocal模式的使用:
使用场景:
在同一个线程的不同开发层次中共享数据。如web应用开发中表示层、业 务层、持久层需要共享数据
使用步骤:
建立一个类,在其中添加一个静态的ThreadLocal变量,使其成为一个共享环境
添加访问静态ThreadLocal的静态方法(设值和取值)
补充信息:
hedwig链式调用(traceid,spanid,parentid)原理:
1、HedwigContextUtil其实是分布式传值对象,HedwigContextUtil封装了一个ThreadLocal对象InvocationContext,该对象封装了全局的参数parentId、reqId 2、服务调用方RequestHandler、HedwigEventEngine设置服务调用方的ClientBizLog,ClientBizLog封装uniqReqId、reqId等信息 3、重要的服务调用方对象DefaultRequest 4、服务提供方LogedWorkerActor、HedwigHessianSkeleton设置服务提供方的ServerBizLog,ServerBizLog封装uniqReqId、reqId等信息
ThreadLocal这集就这样啦。我每天都会更新,希望喜欢的朋友多加关注和点赞的。感谢大家。