在实现基于redis的分布式锁的时候,有一项功能,就是通过开启异步线程,对还没有unlock的key进行定时刷新,延长时间。
初始版本是New一个线程start。我们知道New一个线程,用了没多久又抛弃,这种方法非常消耗资源。因此我考虑能不能用线程池的方式,来获得线程。
如何配置一个异步线程池,这个我就不多说了,无非是注入一个线程池的bean,@EnableAsync ,@Async等。这些教程非常多,搜一搜就有了。我这里讲一下我遇到的坑。
由于分布式锁的实现类里,有一些成员变量,如针对线程的指针,计算重入次数的计数等,而这些成员变量是不能共享的,所以肯定是不能直接@Component注册为单例的。那能不能注册为其他的类型呢,我们知道,@Component注解还可以通过value指定scope,
多例可不可以呢?多例,也就是说每次调用到这个类的时候,就生成一个新的实例过来。这显然是不符合预期的。另外多例还有一个坑,就是由于这个指针是一直被单例对象(如某个@Service的业务层)持有,因此在没有断开引用的情况下,是不会给出新的对象的。也就是说它在单例对象如Service或者Controller中实际上也是单例的。
其实最符合我们预期的,应该是request这种类型,在一次请求中,分布式锁的对象始终是一个,对于不同的request,是给出不同的分布式锁的对象。考虑到上述prototype的问题,request是否有同样的问题还未可知,我想后面尝试着写一个自定义的request类型的scope,看下有没有问题。
好,上述讲完了分布式锁为什么至少要是request类型的,那么接下来,就是遇到的坑了。
如下方式会使@Async失效[1]
异步方法使用static修饰
异步类没有使用@Component注解(或其他注解)导致spring无法扫描到异步类
异步方法不能与被调用的异步方法在同一个类中
类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象
如果使用SpringBoot框架在启动类或配置类中增加@EnableAsync注解
因为分布式锁本身是没有注入spring,它里面的执行异步任务的异步类对象(在分布式锁中为一个成员变量,new出来的),由于也拥有布尔类型的成员变量而无法注入,所以异步会失效变为同步!
当然了,即使异步类能够注入到spring中,但是在以@Autowired的方式注入到分布式锁中,你在new出分布式锁后,debug到分布式锁中,会看到成员变量-异步类为Null。原因我猜测是没有注入spring的类,你new出来后,其中autowired的对象无法生效!
综上所述,问题导致现在分布式锁卡在了这里,似乎使用线程去异步刷新锁的时间是一个不可能完成的任务,除非你舍得用new一个线程然后start的方式去执行线程!问题我会在想想,如果有新的进展我这里会更新,如果有大佬看到了,也请大佬不吝赐教,是否有能够完成这种需求的方案~
参考文章:
[1],springboot 开启一个异步线程