可重新加载的缓存

时间:2009-11-02 16:31:19

标签: java caching

我需要在servlet的内存中存储查找映射。应该从文件中加载映射,并且每当文件更新时(通常不是这样),都应该在执行查找的同一个线程中重新加载映射。

但我不确定如何以线程安全的方式实现此功能。我想确保重新加载不会发生多次。

public class LookupTable
{

    private File file;
    private long mapLastUpdate;
    private Map<String, String> map;

    public String getValue(String key)
    {
        long fileLastUpdate = file.lastModified();
        if (fileLastUpdate > mapLastUpdate)
        {
         // Only the first thread should run the code in the synchronized block.
         // The other threads will wait until it is finished. Then skip it.

            synchronized (this)
            {
                Map newMap = loadMap();
                this.map = newMap;
                this.mapLastUpdate = fileLastUpdate;
            }
        }

        return map.get(key);
    }

    private Map<String, String> loadMap()
    {
        // Load map from file.
        return null;
    }
}

如果有人对已经解决这个问题的外部图书馆有任何建议,那也可以。我快速浏览了一些缓存库,但找不到我需要的东西。

由于

4 个答案:

答案 0 :(得分:3)

我建议您使用imcache。请使用以下缓存加载器构建并发缓存,

Cache<String,LookupTable> lookupTableCache = CacheBuilder.
   concurrentHeapCache().cacheLoader(new CacheLoader<String, LookupTable>() {
   public LookupTable load(String key) {
   //code to load item from file.
   }
}).build();

答案 1 :(得分:2)

正如z5h所建议的,您需要通过用于保持文件重新加载原子的相同锁来保护您的条件(fileLastUpdate&gt; mapsLastUpdate)。

我想到这个问题的方法是查看类中的所有成员变量,并找出他们需要的线程安全保证。在你的情况下,没有成员(文件,长,HashMap - 好吧,我假设HashMap)是线程安全的,因此它们都必须受到锁的保护。他们也都参与了一个不变量(它们都在一起变化),所以它们必须受到SAME锁的保护。

您的代码,更新并使用annotations(这些只是信息,它们不强制执行任何操作!)Java Concurrency In Practice建议(所有Java开发人员应阅读的优秀书籍:)) / p>

/**
* Lookup table that automatically reloads itself from a file
* when the filechanges.
*/
@ThreadSafe
public class LookupTable
{
    @GuardedBy("this")
    private long mapLastUpdate;
    @GuardedBy("this")
    private final File file;
    @GuardedBy("this")
    private Map<String, String> map;

    public LookupTable(File file)
    {
        this.file = file;
        this.map = loadMap()
    }

    public synchronized String getValue(String key)
    {
        long fileLastUpdate = file.lastModified();
        if (fileLastUpdate > this.mapLastUpdate)
        {
            // Only the first thread should run the code in the synchronized block.
            // The other threads will wait until it is finished. Then skip it.
            Map newMap = loadMap();
            this.map = newMap;
            this.mapLastUpdate = fileLastUpdate;
        }
        return map.get(key);
    }

    private synchronized Map<String, String> loadMap()
    {
        // Load map from file.
        return null;
    }
}

这是安全的,但它是完全同步的:只有一个线程一次在地图中进行查找。如果您需要查找并发,则需要更复杂的方案。实现将取决于是否允许线程在加载新版本时查看旧版本的查找表,等等。

如果您将地图成员设为最终成员,并使用ReadWriteLock对其进行保护,则可能会有一些爆炸声。很难用这里有限的信息预测你对此锁的争论程度。

答案 2 :(得分:0)

如果文件实际上是properties file格式(#行作为注释,键=值行作为键/值对),请考虑使用java.util.ResourceBundle。其默认ResourceBundle.Control实现将以特定间隔自动重新加载文件。您甚至可以使用自定义实现覆盖它。

祝你好运。

答案 3 :(得分:0)

您的检查需要在同步块中。 否则,几个线程可以读取(fileLastUpdate&gt; mapLastUpdate)为true,然后是更新代码上的所有块。最糟糕的是两个世界。