java中ThreadLocal的应用场景实例分析

 更新时间:2021年02月17日 10:16:06   作者:小妮浅浅  
在本篇文章里小编给大家整理的是一篇关于java中ThreadLocal的应用场景实例分析,对此有兴趣的朋友们可以学习参考下。

说到线程的安全,我们可以通过ThreadLocal来解决。但作为一种强大的变量,它的应用场景远不止如此。在各类的框架中,我们依然可以使用来对它们进行管理。同时在使用ThreadLocal时需要注意内存泄漏的问题。下面我们就这两点进行分析,并带来对应代码的展示。

1、各种框架中的应用

Spring框架的事务管理中使用ThreadLocal来管理连接,每个线程是单独的连接,当事务失败时不能影响到其他线程的事务过程或结果,还有大家耳闻目睹的ORM框架、Mybatis也是用ThreadLocal管理,SqlSession也是如此。

//Spring TransactionSynchronizationManager类
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
  DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
  Connection con = null;
  try {
    //此处省略N行代码
    if (txObject.isNewConnectionHolder()) {
      //绑定数据库连接到线程中
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
    }
  }
  catch (Throwable ex) {
    if (txObject.isNewConnectionHolder()) {
      //当发生异常时,移除线程中的连接
      DataSourceUtils.releaseConnection(con, obtainDataSource());
      txObject.setConnectionHolder(null, false);
    }
    throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
  }
}

2、防止内存泄漏

通常我们是使用如下的方式操作ThreadLocal,在使用完threadlocal后一定要remove掉,防止内存泄露。

private static final ThreadLocal<LoginUser> loginUserLocal = new ThreadLocal<LoginUser>();
public static LoginUser getLoginUser() {
  return loginUserLocal.get();
}
public static void setLoginUser(LoginUser loginUser) {
  loginUserLocal.set(loginUser);
}
public static void clear() {
  loginUserLocal.remove();
}
//在使用完后一定要清理防止内存泄露
try{
  loginUserLocal.set(loginUser);
  //执行其他业务逻辑
}finally{
  loginUserLocal.remove();
}

java中ThreadLocal实例扩展:

/**
 * 日期工具类(使用了ThreadLocal获取SimpleDateFormat,其他方法可以直接拷贝common-lang)
 * @author Niu Li
 * @date 2016/11/19
 */
public class DateUtil {

 private static Map<String,ThreadLocal<SimpleDateFormat>> sdfMap = new HashMap<String, ThreadLocal<SimpleDateFormat>>();

 private static Logger logger = LoggerFactory.getLogger(DateUtil.class);

 public final static String MDHMSS = "MMddHHmmssSSS";
 public final static String YMDHMS = "yyyyMMddHHmmss";
 public final static String YMDHMS_ = "yyyy-MM-dd HH:mm:ss";
 public final static String YMD = "yyyyMMdd";
 public final static String YMD_ = "yyyy-MM-dd";
 public final static String HMS = "HHmmss";

 /**
  * 根据map中的key得到对应线程的sdf实例
  * @param pattern map中的key
  * @return 该实例
  */
 private static SimpleDateFormat getSdf(final String pattern){
  ThreadLocal<SimpleDateFormat> sdfThread = sdfMap.get(pattern);
  if (sdfThread == null){
   //双重检验,防止sdfMap被多次put进去值,和双重锁单例原因是一样的
   synchronized (DateUtil.class){
    sdfThread = sdfMap.get(pattern);
    if (sdfThread == null){
     logger.debug("put new sdf of pattern " + pattern + " to map");
     sdfThread = new ThreadLocal<SimpleDateFormat>(){
      @Override
      protected SimpleDateFormat initialValue() {
       logger.debug("thread: " + Thread.currentThread() + " init pattern: " + pattern);
       return new SimpleDateFormat(pattern);
      }
     };
     sdfMap.put(pattern,sdfThread);
    }
   }
  }
  return sdfThread.get();
 }

 /**
  * 按照指定pattern解析日期
  * @param date 要解析的date
  * @param pattern 指定格式
  * @return 解析后date实例
  */
 public static Date parseDate(String date,String pattern){
  if(date == null) {
   throw new IllegalArgumentException("The date must not be null");
  }
  try {
   return getSdf(pattern).parse(date);
  } catch (ParseException e) {
   e.printStackTrace();
   logger.error("解析的格式不支持:"+pattern);
  }
  return null;
 }
 /**
  * 按照指定pattern格式化日期
  * @param date 要格式化的date
  * @param pattern 指定格式
  * @return 解析后格式
  */
 public static String formatDate(Date date,String pattern){
  if (date == null){
   throw new IllegalArgumentException("The date must not be null");
  }else {
   return getSdf(pattern).format(date);
  }
 }
}

到此这篇关于java中ThreadLocal的应用场景实例分析的文章就介绍到这了,更多相关java中ThreadLocal的应用场景浅析内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • springboot使用alibaba的druid数据库连接池错误的问题及解决

    springboot使用alibaba的druid数据库连接池错误的问题及解决

    这篇文章主要介绍了springboot使用alibaba的druid数据库连接池错误的问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • Java中PreparedStatement的用法解析

    Java中PreparedStatement的用法解析

    这篇文章主要介绍了Java中PreparedStatement的用法解析,在JDBC应用中,PreparedStatement是一种比Statement更好的选择,PreparedStatement可以通过使用参数化查询来避免SQL注入攻击,并且可以提高查询性能,需要的朋友可以参考下
    2023-09-09
  • Spring多线程事务处理解决方案

    Spring多线程事务处理解决方案

    这篇文章主要介绍了Spring多线程事务处理,本文主要介绍了spring多线程事务的解决方案,心急的小伙伴可以跳过上面的理论介绍分析部分直接看最终解决方案,需要的朋友可以参考下
    2024-03-03
  • idea中使用Inputstream流导致中文乱码解决方法

    idea中使用Inputstream流导致中文乱码解决方法

    很多朋友遇到一个措手不及的问题当idea中使用Inputstream流导致中文乱码及Java FileInputStream读中文乱码问题,针对这两个问题很多朋友不知道该如何解决,下面小编把解决方案分享给大家供大家参考
    2021-05-05
  • 解决MyBatis中为类配置别名,列名与属性名不对应的问题

    解决MyBatis中为类配置别名,列名与属性名不对应的问题

    这篇文章主要介绍了解决MyBatis中为类配置别名,列名与属性名不对应的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-11-11
  • MyBatis中传入参数parameterType类型详解

    MyBatis中传入参数parameterType类型详解

    这篇文章主要给大家介绍了关于MyBatis中传入参数parameterType类型的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2018-04-04
  • 解读Spring框架中常用的设计模式

    解读Spring框架中常用的设计模式

    这篇文章主要介绍了解读Spring框架中常用的设计模式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • 通过字节码看java中this的隐式传参详解

    通过字节码看java中this的隐式传参详解

    这篇文章主要给大家介绍了关于如何通过字节码看java中this的隐式传参的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-11-11
  • Java程序员必备的11大IntelliJ插件(附地址)

    Java程序员必备的11大IntelliJ插件(附地址)

    这篇文章主要介绍了Java程序员必备的11大IntelliJ插件(附地址),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • Java访问权限控制的重要性深入讲解

    Java访问权限控制的重要性深入讲解

    这篇文章主要给大家介绍了关于Java访问权限控制的重要性的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-11-11

最新评论