我有缓存列表,我的代码看起来像这样
public class MyList {
private final List<String> cache = new ArrayList<String>();
private List<String> loadMyList() {
// HEAVY OPERATION TO LOAD DATA
}
public List<String> list() {
synchronized (cache) {
if( cache.size() == 0 ) {
cache.addAll(loadMyList());
}
return Collections.unmodifiableList(cache);
}
}
public void invalidateCache() {
synchronized (cache) {
cache.clear();
}
}
}
因为列表加载非常繁重,我收到一个请求,如果列表加载当前正在进行中,则返回“旧”缓存数据...
这是可能的,有人可以给我指点如何从这里开始
编辑:Adam Horvath和bayou.io吸食这样的东西
public class MyList
{
private final List<String> cache = new ArrayList<String>();
private final List<String> oldCache = new ArrayList<String>();
private volatile boolean loadInProgress = false;
private List<String> loadMyList()
{
// HEAVY OPERATION TO LOAD DATA
}
public List<String> list()
{
synchronized (cache)
{
if( loadInProgress )
return Collections.unmodifiableList( oldCache );
else
return Collections.unmodifiableList(cache);
}
}
public void invalidateCache()
{
synchronized (cache)
{
// copy to old cache
oldCache = new ArrayList<String>( cache );
// set flag that load is in progress
loadInProgress = true;
// clear cache
cache.clear();
// initialize load in new thread
Thread t = new Thread(new Runnable()
{
public void run()
{
cache.addAll( loadMyList() );
// set flag that load is finished
loadInProgress = false;
}
});
t.start();
}
}
}
这个编辑过的代码会有任何问题吗? 由于我在多线程和/或兑现优化方面没有经验,我将不胜感激任何性能建议
答案 0 :(得分:2)
“因为列表加载非常繁重,我收到一个请求,如果列表加载当前正在进行中,我将返回”旧“缓存数据......”
由于您的“同步(缓存)”块,这不会发生。您需要一个volatile布尔标志(mutex)来告诉您正在生成List。当Thread尝试获取list()并且mutex为true时,它将接收Cached。在loadMyList()完成时将其设置为false。
因此,删除synchronized块并开始在单独的Thread中加载列表。
public class MyList {
private List<String> cache = new ArrayList<String>();
private volatile boolean loadInProgress = false;
private List<String> loadMyList() {
// HEAVY OPERATION TO LOAD DATA
}
public List<String> list() {
// Whatever is in cache, you can always return it
return Collections.unmodifiableList(cache);
}
/**
* Starts the loader-thread and then continues.
*/
public void invalidateCache() {
// Next two lines make sure only one Loader-thread can be started at the same time
synchronized (cache) {
if (!loadInProgress) {
// initialize load in new thread
Thread t = new Thread("Loader-thread") {
@Override
public void run() {
List<String> toAssign = loadMyList();
// You can simply assign instead of copying
cache = toAssign;
// cache now "points to" refreshed list
loadInProgress = false;
}
};
loadInProgress = true;
t.start();
// Now let's exit the synchronized block. Hopefully the Thread will start working soon
} else {
// A Thread is already working or about to start working, don't bother him
}
}
}
}