我需要在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;
}
}
如果有人对已经解决这个问题的外部图书馆有任何建议,那也可以。我快速浏览了一些缓存库,但找不到我需要的东西。
由于
答案 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,然后是更新代码上的所有块。最糟糕的是两个世界。