天天看点

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的数据库连接池源码实现:

ThreadLocal应用场景
ThreadLocal应用场景
ThreadLocal应用场景
ThreadLocal应用场景

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

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

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

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

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

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

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

假如我们把SimpleDateFormat定义成static成员变量,那么多个thread之间会共享这个sdf对象, 所以Calendar对象也会共享。 

假定线程A和线程B都进入了parse(text, pos) 方法, 线程B执行到calendar.clear()后,线程A执行到calendar.getTime(), 那么就会有问题。

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

附-SimpleDateFormat关键源码:

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

继续阅读