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

ffdz8vbo  于 2021-07-06  发布在  Java
关注(0)|答案(1)|浏览(331)

**结束。**此问题需要详细的调试信息。它目前不接受答案。
**想改进这个问题吗?**更新问题,使其成为堆栈溢出的主题。

20天前关门了。
改进这个问题
我不知道如何实现静态变量的刷新。此静态变量将被项目的所有模块用作全局变量。它是一个布尔值,用于存储任务状态-已完成或未完成。
在初始化时,此值设置为 false 将设置为 true 很快,一旦 Cache 是建的。
项目如下所示:

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处理数据并创建 Event 存储在数据库中然后再存储在缓存中的对象。在数据库中存储数据是必须的,缓存有助于极大地限制对数据库的请求。
在初始化时,事件管理器有一个特定的任务:它从数据库中的表中获取所有数据并将它们存储在缓存中。
模块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都必须知道缓存是否可访问。一旦所有的数据都从数据库中加载,它就会被删除。
一旦所有数据都存储在缓存中,事件管理器就会将布尔值设置为true。

public class OverallStatus {

    public  static volatile boolean cacheLoaded;
...

模块1->n必须读取此值。问题是它总是返回false。
在调试模式下,我可以看到事件管理器将此值设置为 true ,但其他模块总是将其作为 false .
我首先认为可以用以下事实来解释 executorService 在内存中保留在初始化时存在的假值,当它被调度时。
所以我加了 volatile 关键字 static 值,以便每次 executorService 启动它的任务。
我不明白为什么 OverallStatus 对象的行为是这样的。鉴于其价值已从 falsetrue 通过事件管理器,所有其他模块都可以通过 false 价值,就好像它是一个完全不同的背景。
有人能解释这样的行为吗?也许,能找到什么样的解决办法来逃脱惩罚?我试着在甲骨文的文档中找到一些与我的问题相关的东西,但也许我没有理解所有的细节。

sf6xfgos

sf6xfgos1#

可能是“模块多加载”。
一个问题的关键 static 当然,变量就是整个类只有一个变量,而不是“每个示例一个”。
然而,我们必须放大“类”的实际含义。它的意思不是“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 加载数百次:它询问其父加载程序,即系统加载程序,因此只有一个string类:一个systemloader(已加载)。
但是,你不必这样做;有些类加载器故意不询问父类,并且总是自己加载它,如果所讨论的类根本不在类路径上,那么显然类加载器最终将完成这项工作。
考虑到你使用的是“模块”,很可能就是这样。像osgi这样的运行时模块系统广泛使用类加载器。对于试图为实时重新加载提供运行时支持的系统也是如此;有些Web服务器允许您直接放入一个新的jar,然后在不重新启动服务器的情况下自动“更新”。有几种方法可以做到这一点;最流行的方法是注意到jar文件发生了变化,然后为它启动一个新的类加载器:瞧,新类。
一个简单的方法来测试是否发生了这种情况?这个 java.lang.Class 表示类的示例将不同。所以,打印它的系统hashcode并进行比较。
在将静态变量设置为true的代码中,执行以下操作: OverallStatus.cacheLoaded = true; LOG.log("Set cacheLoaded to true for OS with sID: {}", System.identityHashCode(OverallStatus.class)); . 然后在检查cacheloaded的代码中,添加 LOG.log("Checking cacheLoaded for OS with sID: {}", System.identityHashCode(OverallStatus.class)); .
我打赌你会得到不同的数字。
在这种情况下,请阅读模块系统的手册。或者,问另一个问题,如何确保某个类只被加载一次,即使是对于多个分隔模块,也要提到模块化的过程。

相关问题