记录一下最近工作中出现的两个多线程的问题

1.项目中是存在多线程任务的,实现方式是使用springboot自带的注解来实现的

@Override
    @Async("analyseThreadPool")
    public void refreshProgress(TbProgress tbProgress){
// do something
  }

正常来说 这个 实现类里的 refreshProgress方法应该是多线程调用的,虽然没实现Future接口,但是应该不影响功能

实际上这里犯了一个问题

 

本身这个类就是用 @Service修饰的 实现类impl中的,然后我为了实现类的调用更加独立,又嵌套了一层实现类

也就是 Aimpl 中 @Autowired 实例化了 Bimpl,并且调用了Bimpl中的方法

这看上去也没什么,但是不幸的是B中的的一个 SeqIdGenerator 类 是使用@Autowired实例化的,这个类的作用是读取redis中存放一个全局的数值并预支1000个,供全局取,当全部取完了,这个类会再去redis预支1000个。

但是当多线程中,这个值的获取并不能保证是线程安全的。

按照代码规范,在多线程的接口实现类中,所有的类中的全局变量都应该使用 ThreadLocal 来修饰,具体的例如:

private static ThreadLocal<Date> threadLocal = new ThreadLocal<>();

这样可以保证,每个线程不共享这个变量,即这个变量在每个线程中独享一份分身。当然前提是这个类是单例的,才会存在线程安全问题(springboot的@Service就是单例)

所以在这里,使用了@prototype去修饰这个接口的实现类,试图让他变成多例,来阻止SeqIdGenerator 的产生线程安全问题。

很显然,这是奏效的。

 

但是不幸的,由于前面提到的” 实现类的嵌套关系 “

@prototype注解只被加在了 Bimpl中,而没有加入在 Aimpl中。

导致每次多线程运行这个功能,总会偶发异常。

解决方案是:

MiningMessageService miningMessageService = SpringUtils.getBean(MiningMessageService.class);

使用SpringUtils去获取bean的实例,这样就可以保证多例。

 

2.多线程任务中,曾经有前置的删除操作(进行独立任务之前,先清空相关数据),

当时因为不是多线程的,所以使用了 spring自带的事务注解 @Transactional 

但是’多线程‘和‘事务’,两个概念其实不是兼容的,甚至是有矛盾的!

所以把@Transactional 先注掉,问题解决

 

posted @ 2022-04-24 20:16  佩洛君  阅读(59)  评论(0编辑  收藏  举报