ThreadLocal的使用笔记

背景:在项目中有一个关于异步批量支付的功能,这个功能需要在前台立即返回信息,并且可以批量处理多笔数据,在实现这个业务需求时,我首先是根据支付通道的类型,判断调用不同的异步支付方法,开启新的线程调用相应的支付方法,在这个具体的支付方法中,按照支付的批次查询出需要进行操作的数据,对于这些数据,通过for循环调用对应的第三方支付的帮助客户端,就是在这个地方调用时需要做到同步操作,需要实时返回数据,去更新每一条锁定的数据的记录,此外有额外的定时器负责定时冲正扫描每个批次中的支付结果,若批次中每一笔都支付成功,就更新批次的支付结果。

问题:原本的设计是将第三方支付的客户端方法设定为静态方法,方便工具化调用,如下图所示:

支付的工具类

对于客户端的调用者来说,很多请求都是公共的,只需要传入必要的少数参数即可完成支付操作。原本这样的设计是没什么问题的,但现在调用第三方的请求接口时,会出现支付结果为未知,需再次查询的情况。处理逻辑如下图所示:

 

未知结果的处理逻辑

因此在处理调用第三方的接口后需要进行轮询操作查询结果。于是产生了如下所示的第一版代码:

 

第一版代码-调用

 

第一版代码-轮询操作

其实这是第二版的代码,第一版的代码中response对象是用statsic关键字和voliate关键字修饰的类变量,并且轮询判断是在方法体外,无需传入response变量进行递归调用;结果发现使用static关键字会导致多次执行出错,也就是在执行下一次操作前必须清空response的值,在多线程环境下,这种操作不靠谱,因此改成了如上图所示的第二版代码。在上图所示代码中,采用每次轮询前先创建一个空的对象,然后传入轮询的方法中,通过判断入参,决定是否继续回调自身。但这种方式比较不够优雅,这时我突然想到了多线程环境下常用的一个工具类ThreadLocal,结合它的使用,完成下述的第三版代码;

 

第三版代码-ThreadLocal对象

 

第三版代码-方法调用

 

第三版代码-轮询方法

那么我们来分析下,为什么同样是被static关键字修饰的,采用一般的对象就会导致问题,而ThreadLocal对象却不会出现问题呢;那么看一下ThreadLocal的源码:

 

ThreadLoacl-get方法

 

ThreadLocal-set方法

 

 

可以看到ThreadLocal中存储数据其实都是使用的内部的一个Map对象,并且是使用当前线程的名称作为map的key来存储数据(可能描述的不够准确,有兴趣的可以看下源码),因此可以保证多线程环境下,存储的数据互不干扰。而对于我实际项目中的使用情况,尽管对于所有的调用ThreadLoacl都是用的同一个,但他们内部存储的ThreadLocalMap对象却是不一样的,因此,数据并不会产生问题。

总结一下,在多线程的环境下,可以采用类似ThreadLoacl的方式为每个线程保存一份本地的数据,保证这些数据之间互不干扰,这样的使用,可以避免一些加锁的情况,因为锁的力度越大,系统的响应速度越慢,这样不利于系统的整体性能,并且锁的滥用会导致各种各样的问题,因此在多线程开发的环境下,尽量采用一些替代方案来避免锁的使用。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

毕知必会

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值