此类旨在在Spring Boot控制器中运行。管理数据位于Oracle表中,并且只有一个记录。这是必需的,因为数据可能会被另一个应用程序更改,如果确实如此,则该应用程序需要读取新数据。因此AdminData是一个实体bean(休眠)。实际上,管理数据几乎永远不会更新,但这是一个高容量的Web应用程序,因此非常频繁地读取数据。每次调用GET和POST都需要它。我考虑过使用AtomicReference ,但是在这种情况下,我不确定它是否比仅使用volatile关键字更好。我认为这是线程安全的,因为:1-get()方法仅返回引用,在Java中,获取或更新引用是原子的。2-由于对存储库的调用,onDatabaseChangeNotification()调用可能不会自动执行,但是此方法只能由Oracle调用来执行,因此将只有一个线程在运行它。同样,对cachedAd的引用分配应该是原子的。3-我认为对setInitialValue()的调用可能也只会由一个线程执行,但是我不确定,所以我添加了同步对象。我对吗?谢谢你的帮助。@DependsOn("DecLogger")@Servicepublic class AdminDataCacher implements DatabaseChangeListener{ @Autowired private AdminDataRepository adRep; private volatile AdminData cachedAd = null; public AdminData get() { return cachedAd; } @Override public void onDatabaseChangeNotification(oracle.jdbc.dcn.DatabaseChangeEvent e) { cachedAd = adRep.findByKey(1L); DecLogger.DEC_LOGIN.finer(() -> "Oracle DCN Call on Admin Data - Invalidating Cached Data"); } @PostConstruct private synchronized void setInitialValue() { cachedAd = adRep.findByKey(1L); DecLogger.DEC_LOGIN.finer(() -> "AdminDataCacher - Initial value set"); }}更新,基于注释和一些睡眠:如果AdminData不是线程安全的,并且无法将其设置为线程安全的(通过使其变为不可变的),则尽管我担心性能,但这种方法还是可行的: public AdminData get() { AdminData tmp = cachedAd; return tmp.clone(); }另一个更新基于更多的评论和更多的研究,我重写了课堂。我决定需要一个不可变的对象来保存管理数据,因此我创建了一个额外的不可变的类,称为AdminDataImmutable。由于该类是不可变的,因此它本质上是线程安全的,因此我可以将其返回给每个调用者,从而避免了克隆缓存实例的开销,并且我不必担心将来会有其他开发人员滥用它,因此我将'不必保护/保护其中的副本。如所指出的,当数据库发生更改时,我应该在存储库上进行同步,并且可以不用担心更新缓存对象的引用,因为在Java中,引用更新是设计使然的。所以现在,在get()方法中,我可以简单地返回引用。下面的代码。这个新版本有意义吗???谢谢!@DependsOn("DecLogger")@Servicepublic class AdminDataCacher implements DatabaseChangeListener{ private volatile AdminDataImmutable cachedAd; @Autowired private AdminDataRepository adRep; public AdminDataImmutable get() { return cachedAd; } @Override public void onDatabaseChangeNotification(oracle.jdbc.dcn.DatabaseChangeEvent e) { DecLogger.DEC_LOGIN.finer(() -> "Oracle DCN Call on Admin Data - Invalidating Cached Data"); synchronized(adRep) { AdminDataEntity ade = adRep.findByKey(1L); cachedAd = new AdminDataImmutable(ade); } } @PostConstruct private void loadInitialValue() { synchronized(adRep) { AdminDataEntity ade = adRep.findByKey(1L); cachedAd = new AdminDataImmutable(ade); } }}最后更新我使cacheadAd易失。 (adsbygoogle = window.adsbygoogle || []).push({}); 最佳答案 我认为volatile AdminData在这里没有用,因为这只能安全地更新引用,而不能使AdminData对象线程本身安全。就像您提到的get()方法一样,在Java中,引用的更新始终是原子操作。因此,您尝试过度保护参考。如果要确保AdminData对象本身是线程安全的,则应查看AdminData对象的代码。关于2)和3),我会注意到也许值得看一下findByKey方法的代码并使它成为线程安全的,但不要尝试在调用者线程上使用数字(似乎您不确定两者是否都可以)情况)。尝试通过堆栈使线程安全代码尽可能地高-这将减少关键部分的数量并降低代码复杂性。如果您无法重做或查看AdminDataRepository的代码,则在2)的情况下,您将假定只有一个调用者。但是以类推3)也许值得添加synchronized,因为至少有两个线程同时调用findByKey仍然有可能被一个调用:至少一个来自非线程安全(但可能会有更多线程),并从同步的onDatabaseChangeNotification()进行一次调用。因此,您可以保护一种方法,但仍然可以对setInitialValue()进行两次并发调用。如果findByKey()与adRep对象中的某些共享数据进行交互,而不仅仅是从Oracle数据库中检索数据,则可能会导致问题(作为简单示例,请想象,每次findByKey()调用都会增加一些内部计数器,该计数器在所有调用之间共享) 。接下来,将findByKey放在方法synchronized上还有一个陷阱。在这种情况下,您将onDatabaseChangeNotification()对象(this对象)用作锁定对象,并且在AdminDataCacher仅在AdminDataRepository adRep中注入的情况下它将保持安全。但是,如果将同一个singelton对象AdminDataCacher注入到某个类中-您会遇到麻烦,因为AdminDataRepository adRep是无用的,您仍然可能同时获得对synchronized的多次调用(一个来自adRep.findByKey(),另一个来自更多来自注入了AdminDataCacher的其他类的信息。在这种情况下,您应该保护AdminDataRepository adRep对象:@Overridepublic void onDatabaseChangeNotification(oracle.jdbc.dcn.DatabaseChangeEvent e){ synchronized(adRep) { cachedAd = adRep.findByKey(1L); } DecLogger.DEC_LOGIN.finer(() -> "Oracle DCN Call on Admin Data - Invalidating Cached Data");}@PostConstructprivate void setInitialValue(){ synchronized(adRep) { cachedAd = adRep.findByKey(1L); } DecLogger.DEC_LOGIN.finer(() -> "AdminDataCacher - Initial value set");}对不起,给您来信太多,但我尝试给您一个思路,以分析代码并选择正确的决定。因此,总而言之,分步进行:尝试使adRep对象成为线程安全的并调用它没有AdminDataRepository如果不可能,请使用对象synchronized作为锁使用不带adRep的AdminData对象,但是它也是值得回顾其内部代码附言此信息在纯Java情况下有效。我不是100%肯定Spring没有一些内部逻辑来确保对bean线程的调用安全。 (adsbygoogle = window.adsbygoogle || []).push({}); 08-28 23:01