Java 为什么volatile静态变量不';不刷新?

Java 为什么volatile静态变量不';不刷新?,java,static,Java,Static,我不知道如何实现静态变量的刷新。该静态变量将被项目的所有模块用作全局变量。它是一个布尔值,用于存储任务状态-完成或撤消 在初始化时,该值被设置为false,一旦缓存建立,该值将很快被设置为true 该项目如下所示: Project |-- Domain module (entities & DAO) |----- Services |----- Cache |----- class with static v

我不知道如何实现静态变量的刷新。该静态变量将被项目的所有模块用作全局变量。它是一个布尔值,用于存储任务状态-完成或撤消

在初始化时,该值被设置为
false
,一旦
缓存
建立,该值将很快被设置为
true

该项目如下所示:

Project
|-- Domain module (entities & DAO)
      |----- Services
                 |----- Cache
                 |----- class with static variable 'done / undone' (just to try, not definitive)
|-- Events Manager module (push data to Database & Cache)
|-- module 1 (pull data mainly from Cache, little from DB)
|-- module 2 (pull data mainly from Cache, little from DB)
|-- module 3 (pull data mainly from Cache, little from DB)
EventsManager处理数据并创建存储在数据库和缓存中的
事件
对象。在数据库中存储数据是必须的,缓存有助于极大地限制对数据库的请求

在初始化时,Events Manager有一个特定的任务:它从数据库中的表中获取所有数据并将它们存储在缓存中

模块1->N正在等待事件处理。他们定期(稍微)或(主要)在缓存中查找要处理的新事件

所有模块1->N以相同的方式构建,并在初始化时运行此方法:

...
@Override
    public void run(String... args) {

        LOG.info(String.format("Starting %s Processor", PROCESSOR));

        /*
         * Every 30 seconds, fetches newest Event from Database
         */
        executorService.scheduleAtFixedRate( () -> {
            List<Event> eventFlux = getEventFlux();
            updateLastTimeValue(eventFlux);
            ConnectableFlux<Event> connectableFlux = Flux.fromIterable(eventFlux).publish();
            processEvent(connectableFlux);
            connectableFlux.connect();
        }, 0, 30, TimeUnit.SECONDS);
    }
...
模块1->N必须读取此值。问题是它总是返回false

在调试模式下,我可以看到Events Manager将此值设置为
true
,但其他模块总是将其读取为
false

我首先认为这可以通过以下事实来解释:
executorService
在内存中保留了在初始化时存在的错误值,而当时它已经被安排好了

这就是为什么我将
volatile
关键字添加到
static
值中,以便在每次
executorService
启动其任务时刷新该值

我不明白为什么
OverallStatus
对象的行为是这样的。虽然事件管理器已将其值从
false
更改为
true
,但所有其他模块仍使用
false
值查看它,就像它是一个完全不同的上下文一样


有人能解释这样的行为吗?也许,能找到什么样的解决办法来摆脱它?我试图在中找到与我的问题相关的东西,但可能我没有理解所有的细节。

可能是“模块多加载”

当然,
静态
变量的要点是,整个类只有一个变量,而不是“每个实例一个”

然而,我们必须放大“类”的实际含义。它的意思是com.foo.lovegiver.Main

对于“完全限定类名”和“加载它的类加载器”的每个组合,都有一个类(因此,一个代表该类的
java.lang.class

换句话说,可以加载两个不同的类,它们都是相同的,都被命名为'com.foo.lovegiver.Main',或者完全分开。您可以拥有其中一个的实例。您可以尝试将其中一个实例分配给类型为“Main x”的变量,其中“Main”是“com.foo.lovegiver.Main”的缩写,您会得到一个神秘的错误:“com.foo.lovegiver.Main的实例不能分配给com.foo.lovegiver.Main类型的变量”

两个名称相同但不同的加载程序类中的每一个都有自己的静态变量版本

要得到这样一个奇怪的场景,您需要2个类加载器。每个加载程序都可以加载任何类的自己的版本,以及自己的静态变量

让我们来看看“什么是类加载器”?它不仅仅是调用一些
java.lang.ClassLoader
实例的
loadClass
方法。因为
loadClass
最终可能会要求其他类加载器来完成这项工作。任何给定类的加载器是:实际加载它的任何类加载器,其定义依次为:调用字节码上的本机
defineClass
方法的

类加载器的常见设计是首先询问父类(这就是一个带有类加载器的系统如何加载
java.lang.String
数百次:它询问其父类加载器,即系统加载器,因此只有一个字符串类:加载的一个系统加载器)

然而,你不必这么做;有些类加载器故意不询问父类,总是自己加载,而且[B]如果所讨论的类根本不在类路径上,那么显然,类加载器最终将完成这项工作

考虑到您使用的是“模块”,很可能就是这样。OSGi等运行时模块系统广泛使用类加载器。尝试为实时重新加载提供运行时支持的系统也是如此;有些Web服务器只允许您插入一个新的jar,然后将自动“更新”,而无需重新启动服务器。有几种方法可以做到这一点;最流行的方法是注意jar文件的更改,然后为其启动一个新的类加载器:瞧,新类

一个简单的方法来测试这是否正在进行?表示该类的
java.lang.Class
实例将不同。因此,打印其系统哈希代码并进行比较

在将静态变量设置为true的代码中,执行:
OverallStatus.cacheload=true;LOG.LOG(“对于sID为{}的操作系统,将cacheLoaded设置为true”,System.identityHashCode(OverallStatus.class))。然后在检查cacheLoaded的代码中,添加
LOG.LOG(“检查具有sID:{}的操作系统的cacheLoaded”,System.identityHashCode(OverallStatus.class))

我打赌你会得到不同的数字

在这种情况下,请阅读模块系统的手册。或者,问另一个问题,并询问如何确保即使对于多个分隔符模块,也只加载一次特定类,并提及模块化的操作。

是否将其反射设置为
true
?编译器可以自由内联原语
public class OverallStatus {

    public  static volatile boolean cacheLoaded;
...