Java 线程安全缓存,如果正在获取新缓存,则返回旧缓存

标签 java multithreading performance

我不得不涉足缓存和多线程(每个请求的线程),我是该领域的绝对初学者,所以任何帮助将不胜感激

我的要求是:

  • 缓存一个具有以太网间隔刷新或来自用户的刷新的单个大对象
  • 因为检索对象数据非常耗时所以线程安全
  • 检索对象数据时返回“旧数据”,直到有新数据可用
  • 对其进行优化

从 SO 和其他一些用户帮助我得到了这个 ATM:

** 根据 Sandeep 和 Kayaman 的建议进行编辑 **

public enum MyClass
{
    INSTANCE;

    // caching field
    private CachedObject cached = null;

    private AtomicLong lastVisistToDB = new AtomicLong();
    private long refreshInterval = 1000 * 60 * 5;

    private CachedObject createCachedObject()
    {
        return new CachedObject();
    }

    public CachedObject getCachedObject()
    {
        if( ( System.currentTimeMillis() - this.lastVisistToDB.get() ) > this.refreshInterval)
        {
            synchronized( this.cached )
            {
                if( ( System.currentTimeMillis() - this.lastVisistToDB.get() ) > this.refreshInterval)
                {
                    this.refreshCachedObject();
                }
            }
        }

        return this.cached;
    }

    public void refreshCachedObject()
    {
        // This is to prevent threads waiting on synchronized from re-refreshing the object     
        this.lastVisistToDB.set(System.currentTimeMillis());

        new Thread() 
        {
            public void run() 
            {
                createCachedObject();
                // Update the actual refresh time
                lastVisistToDB.set(System.currentTimeMillis());  
            }
        }.start();
    }
}

在我看来,我的代码满足了上述所有书面要求。 (但我不确定)

随着代码很快进入第三方分析,我真的很感激任何关于代码性能和盲点的意见

感谢您的帮助。

编辑:VanOekel 的回答是解决方案,因为我的代码(根据 Sandeep 和 Kayaman 的建议编辑)没有考虑用户触发的 refresh() 的影响在这个多线程环境中

最佳答案

我不会使用 Sandeep 提议的 DCL,而是使用枚举单例模式,因为它是目前惰性初始化单例的最佳方式(而且看起来比 DCL 更好)。

使用了很多不必要的变量和代码,我会简化很多。

private static Object cachedObject;
private AtomicLong lastTime = new AtomicLong();
private long refreshPeriod = 1000;

public Object get() {

    if(System.currentTimeMillis() - lastTime.get() > refreshPeriod) {
        synchronized(cachedObject) {
            if(System.currentTimeMillis() - lastTime.get() > refreshPeriod) {
                lastTime.set(System.currentTimeMillis());    // This is to prevent threads waiting on synchronized from re-refreshing the object
                new Thread() {
                    public void run() {
                        cachedObject = refreshObject();  // Get from DB
                        lastTime.set(System.currentTimeMillis());  // Update the actual refresh time
                    }
                }.start();
            }
        }
    }
    return cachedObject;
}

Speedwise 仍然可以改进一点,但是减少了很多不必要的复杂性。可以删除对 System.currentTimeMillis() 的重复调用,以及两次设置 lastTime。但是,让我们从这个开始吧。

关于Java 线程安全缓存,如果正在获取新缓存,则返回旧缓存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31338509/

相关文章:

java - 使用 java.util.concurrent API 结束线程

php - MYSQL where 子句在大表上性能降低

java - 为什么 .headless 默认为 True?

JavaFX : How to populate data to TableView from different Thread(Netty)

java - 为什么扩展 JerseyTest 与扩展 TestCase 会导致找不到测试

Node.js 在类方法中生成多个线程

mysql - 使用 CROSS JOIN 的超慢查询

java - 理解 jmh 中的不对称

java - 关闭entityManager的最佳方法

java - 整数除法 : How do you produce a double?