ThreadLocal应用场景

                                        ThreadLocal应用场景

1、数据库连接池实现


Class.forName("com.mysql.jdbc.Driver");

java.sql.Connection conn = DriverManager.getConnection(jdbcUrl);


注意:一次Drivermanager.getConnection(jdbcurl)获得只是一个connection,并不能满足高并发情况。因为connection不是线程安全的,一个connection对应的是一个事物。


每次获得connection都需要浪费cpu资源和内存资源,是很浪费资源的。所以诞生了数据库连接池。数据库连接池实现原理如下:


pool.getConnection(),都是先从threadlocal里面拿的,如果threadlocal里面有,则用,保证线程里的多个dao操作,用的是同一个connection,以保证事务。如果新线程,则将新的connection放在threadlocal里,再get给到线程。

将connection放进threadlocal里的,以保证每个线程从连接池中获得的都是线程自己的connection。


Hibernate的数据库连接池源码实现:

2、有时候ThreadLocal也可以用来避免一些参数传递,通过ThreadLocal来访问对象。

比如一个方法调用另一个方法时传入了8个参数,通过逐层调用到第N个方法,传入了其中一个参数,此时最后一个方法需要增加一个参数,第一个方法变成9个参数是自然的,但是这个时候,相关的方法都会受到牵连,使得代码变得臃肿不堪。这时候就可以将要添加的参数设置成线程本地变量,来避免参数传递。

上面提到的是ThreadLocal一种亡羊补牢的用途,不过也不是特别推荐使用的方式,它还有一些类似的方式用来使用,就是在框架级别有很多动态调用,调用过程中需要满足一些协议,虽然协议我们会尽量的通用,而很多扩展的参数在定义协议时是不容易考虑完全的以及版本也是随时在升级的,但是在框架扩展时也需要满足接口的通用性和向下兼容,而一些扩展的内容我们就需要ThreadLocal来做方便简单的支持。

简单来说,ThreadLocal是将一些复杂的系统扩展变成了简单定义,使得相关参数牵连的部分变得非常容易。

3、在某些情况下提升性能和安全。

用SimpleDateFormat这个对象,进行日期格式化。因为创建这个对象本身很费时的,而且我们也知道SimpleDateFormat本身不是线程安全的,也不能缓存一个共享的SimpleDateFormat实例,为此我们想到使用ThreadLocal来给每个线程缓存一个SimpleDateFormat实例,提高性能。同时因为每个Servlet会用到不同pattern的时间格式化类,所以我们对应每一种pattern生成了一个ThreadLocal实例。

public interface DateTimeFormat {

    String DATE_PATTERN = "yyyy-MM-dd";

    ThreadLocal<DateFormat> DATE_FORMAT = ThreadLocal.withInitial(() -> {

        return new SimpleDateFormat("yyyy-MM-dd");

    });

    String TIME_PATTERN = "HH:mm:ss";

    ThreadLocal<DateFormat> TIME_FORMAT = ThreadLocal.withInitial(() -> {

        return new SimpleDateFormat("HH:mm:ss");

    });

    String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";

    ThreadLocal<DateFormat> DATE_TIME_FORMAT = ThreadLocal.withInitial(() -> {

        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    });

}


为什么SimpleDateFormat不安全,可以参考此篇博文:

假如我们把SimpleDateFormat定义成static成员变量,那么多个thread之间会共享这个sdf对象, 所以Calendar对象也会共享。 
假定线程A和线程B都进入了parse(text, pos) 方法, 线程B执行到calendar.clear()后,线程A执行到calendar.getTime(), 那么就会有问题。

如果不用static修饰,将SimpleDateFormat定义成局部变量: 
每调用一次方法就会创建一个SimpleDateFormat对象,方法结束又要作为垃圾回收。加锁性能较差,每次都要等待锁释放后其他线程才能进入。那么最好的办法就是:使用ThreadLocal: 每个线程都将拥有自己的SimpleDateFormat对象副本。

附-SimpleDateFormat关键源码:

public class SimpleDateFormat extends DateFormat {  

    public void parse(String text, ParsePosition pos){  

        calendar.clear(); // Clears all the time fields  

        // other logic ...  

        Date parsedDate = calendar.getTime();  

    }  

}  


abstract class DateFormat{  

    // other logic ...  

    protected Calendar calendar;  

    public Date parse(String source) throws ParseException{  

        ParsePosition pos = new ParsePosition(0);  

        Date result = parse(source, pos);  

        if (pos.index == 0)  

            throw new ParseException("Unparseable date: \"" + source + "\"" ,  

                pos.errorIndex);  

        return result;  

    }  

}  

以上源码实例参考靠自淡淡的倔强的博客csdn博客

原文:http://blog.csdn.net/u012834750/article/details/71646700


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值