springboot启动时,异步线程读取某个类中的静态变量一直读取为空的问题(变量先是空,后赋值)

一.现象

先上伪代码,如下:

@Component
public class SpringBeanUtil {

    public static JedisUtil jedisUtil;

    @Resource
    public void setJedisUtil(JedisUtil jedisUtil) {
        SpringBeanUtil.jedisUtil = jedisUtil;
    }

}
@Component
public class RedisDelayQueue {

    /**
     * 监听定时任务执行
     */
    @PostConstruct
    public void listenDelayTask(){
        //另起线程执行,避免阻塞无法启动
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        executorService.submit(()->{
            while (true){
                if(SpringBeanUtil.jedisUtil!=null){
                    //todo 具体业务
                }
            }
        });
    }
}

springboot在启动时,@PostConstruct修饰的方法在启动时会去执行,内部另起线程去执行业务逻辑,现在的问题是SpringBeanUtil.jedisUtil!=null,这个地方一直是false,也就是SpringBeanUtil.jedisUtil一直是空的,SpringBeanUtil.jedisUtil在项目启动时也是会初始化的,也就是可能的情况是开始是空的,后面是有值的,listenDelayTask方法里面是循环在执行判断,项目在我本地启动执行是正常的,在测试环境就出现问题了。

二.分析

1.在测试环境中@PostConstruct方法没有执行?后加日志打印排除这个问题

2.if(SpringBeanUtil.jedisUtil!=null)一直是false?猜想后突然想到java内存模型中的可见性问题

可见性是一种复杂的属性,因为可见性中的错误总是会违背我们的直觉。通常,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有时甚至是根本不可能的事情。为了确保多个线程之间对内存写入操作的可见性,必须使用同步机制。

JVM程序运行的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间),用于存储线程私有的数据,而Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝的自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存,不能直接操作主内存中的变量,工作内存中存储着主内存中的变量副本拷贝,前面说过,工作内存是每个线程的私有数据区域,因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成,其简要访问过程如下图:

                                                                         摘自https://www.jianshu.com/p/6abcddd04f4e

三.解决

由上述找到原因,需要解决变量在内存中的可见性问题,即变量发生修改后,需要立即同步到其它线程,于是采用Volatile关键字对变量进行修饰。然后测试环境重新发布后,正常执行。

@Component
public class SpringBeanUtil {

    public volatile static JedisUtil jedisUtil;

    @Resource
    public void setJedisUtil(JedisUtil jedisUtil) {
        SpringBeanUtil.jedisUtil = jedisUtil;
    }

}

四.总结

测试环境在项目启动阶段,有个初始化方法中另起一个线程一直读取不到另一个bean中变量更新后的值,一直读取的是初始的值。

这个现象在我本地是不存在的,并且我的另一个项目同样的写法在测试环境也是正常的(没有Volatile关键字修饰),这个确实是比较玄学。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值