大神们是怎么使用ThreadLocal的?

点击↑上方↑蓝色“编了个程”关注我~

每周至少一篇原创文章

这是本公众号的第 18 篇原创文章

这篇文章是关于ThreadLocal的第三篇文章。本文将挑选一些主流的Java开源框架,从源码上分析,大神们是如何使用ThreadLocal的,学习他们的设计思想。

大家可以直接打开github,搜索相应的项目,然后在项目中搜索相关的类,即可看到源代码。

Quartz

Quartz是一个非常知名的开源任务调度系统。

我们要看的源码是Quartz的SimpleSemaphore这个类。它是一个信号量的实现,在生产者-消费者模型里,信号量代表的就是队列里有多少item需要处理。

在信号量的模型里面有一个“等待”操作。当消费者消费完后,会轮询等待。SimpleSemaphore有一个获取锁的方法obtainLock(),我们要看的也是这个方法的内部代码:

大神们是怎么使用ThreadLocal的?_第1张图片

92行的while循环就是去进行轮询操作,while里面的locks是一个HashSet,为true代表这个lockName对应的锁正在被别的线程持有,所以当前线程需要等待。

我们看到,在while循环的外层86行,有一个判断,其实是用到了ThreadLocal。

这个外层的判断起什么作用呢?其实是「判断当前线程是否已经持有了这个锁」。如果持有了,那就直接跳到最后return true了。因为同一个线程,可能有多个程序片段会调用这个获取锁的方法。

可以看到,使用ThreadLocal可以非常高效地判断当前线程的状态,可以快速检测出当前线程是否已经获取了锁,避免了后续锁的检测和争用。

Mybatis

Mybatis不用多说,搞Java的应该都听过或者用过。我们今天要介绍的是它的SqlSessionManager。

Mybatis是一个持久化框架。持久化框架,必然会面临事务的问题。我们的数据库(比如MySQL)可以保证本地事务,但也要求必须在同一个连接才行。

应用程序使用MyBatis,可能会在多个程序片段去访问数据库,做一些增删改查的操作。它们可能需要在同一个事务里面。

举个例子,我们修改完订单状态后,可能还需要修改积分,它们应该在同一个事务里。

Mybatis使用SqlSessionManager保证了我们同一个线程取出来的连接总是同一个。它是如何做到的呢?其实很简单,就是内部使用了一个ThreadLocal。

然后所有的创建连接、取连接都是通过这个ThreadLocal变量的get/set方法进行操作。

private final ThreadLocal localSqlSession = new ThreadLocal<>();


// 创建连接
public void startManagedSession() {
    this.localSqlSession.set(openSession());
}

// 取连接
@Override
public Connection getConnection() {
    final SqlSession sqlSession = localSqlSession.get();
    if (sqlSession == null) {
        throw new SqlSessionException("Error:  Cannot get connection.  No managed session is started.");
    }
    return sqlSession.getConnection();
}  

总结

其实ThreadLocal使用起来是很简单的,这也是ThreadLocal设计的初衷。

使用ThreadLocal,可以保存线程的状态,使得多个程序片段可以很方便地得到当前线程的数据,而不会对其它线程造成影响,也不需要上锁同步。

所以,使用ThreadLocal可以“避免”一些多线程问题,开发安全高效的应用程序。

关于作者

我是Yasin,一个正在学习写作的程序员

微信公众号:编了个程(blgcheng)

个人网站:https://yasinshaw.com

这里很多技术干货,关注肯定不后悔

加个星标可以第一时间看到最新文章

听说,转发在看的人都升职加薪了

推荐阅读

  • 毕业两年多,从国企到阿里,我经历了什么?

  • 如何使用ThreadLocal避免线程安全问题?

  • 什么是ThreadLocal?

  • 一篇短文带你走进Java线程池

  • 一次性把Java的四种引用说清楚!

你可能感兴趣的:(大神们是怎么使用ThreadLocal的?)