Java缓存对象返回旧值
我有一个缓存列表,我的代码如下Java缓存对象返回旧值,java,synchronization,Java,Synchronization,我有一个缓存列表,我的代码如下 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
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();
}
}
}
公共类MyList{
私有最终列表缓存=新的ArrayList();
私有列表loadMyList(){
//加载数据的繁重操作
}
公开名单(){
已同步(缓存){
if(cache.size()==0){
addAll(loadMyList());
}
返回集合。不可修改列表(缓存);
}
}
公共无效{
已同步(缓存){
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();
}
}
}
公共类MyList
{
私有最终列表缓存=新的ArrayList();
私有最终列表oldCache=newarraylist();
私有易失性布尔loadInProgress=false;
私有列表loadMyList()
{
//加载数据的繁重操作
}
公开名单()
{
已同步(缓存)
{
如果(加载进程)
返回集合。不可修改列表(oldCache);
其他的
返回集合。不可修改列表(缓存);
}
}
公共无效
{
已同步(缓存)
{
//复制到旧缓存
oldCache=newarraylist(缓存);
//设置加载正在进行的标志
loadInProgress=true;
//清除缓存
cache.clear();
//在新线程中初始化加载
线程t=新线程(新的可运行线程()
{
公开募捐
{
addAll(loadMyList());
//设置加载完成的标志
loadInProgress=false;
}
});
t、 start();
}
}
}
这个编辑过的代码会有任何问题吗??
由于我在多线程和/或现金优化方面没有经验,我非常感谢所有性能建议“因为列表负载非常重,我收到一个请求,如果列表负载当前正在进行,我将返回“旧”缓存数据…”
这不会发生,因为您的“同步(缓存)”块。您需要一个易失性布尔标志(互斥)来告诉您正在生成一个列表。当线程尝试获取list()并且互斥量为true时,它将接收缓存的互斥量。loadMyList()完成时将其设置为false
因此,删除同步块并开始在单独的线程中加载列表
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
}
}
}
}
公共类MyList{
私有列表缓存=新的ArrayList();
私有易失性布尔loadInProgress=false;
私有列表loadMyList(){
//加载数据的繁重操作
}
公开名单(){
//缓存中的任何内容都可以返回
返回集合。不可修改列表(缓存);
}
/**
*启动加载程序线程,然后继续。
*/
公共无效{
//接下来的两行确保同一时间只能启动一个加载器线程
已同步(缓存){
如果(!loadInProgress){
//在新线程中初始化加载
螺纹t=新螺纹(“加载器螺纹”){
@凌驾
公开募捐{
List toAssign=loadMyList();
//您可以简单地分配而不是复制
cache=toAssign;
//缓存现在“指向”刷新列表
loadInProgress=false;
}
};
loadInProgress=true;
t、 start();
//现在让我们退出同步块。希望线程很快就能开始工作
}否则{
//线程已经在工作或即将开始工作,不要打扰他
}
}
}
}
如果我没有弄错的话,您可能正在寻找我假设您的意思是“缓存”而不是“兑现”在您的示例中,调用list()
的人可以查看您的列表-这意味着,当您清除/重新填充列表时,仍然拥有该视图副本的调用方可以看到处于不稳定状态的列表。你确定你想要吗?另外,在您的示例中没有“旧”数据-列表是空的,或者它有一些内容,但一旦加载,它就不会再次更新…您试图解决的这个实现的问题是什么?如果失效后的第一个读卡器负责加载数据,它将阻塞读卡器的线程。这可能是不可取的;我们希望所有的读者都能尽快返回。因此,最好是失效立即触发加载,可能是在新线程中。如果在加载仍在进行时出现新的失效,我们必须安排另一次加载,并确保稍后缓存第二个结果。单独的状态标志似乎不是一个好的设计选择。这里有很多你不需要的选择。(例如,参见上面的Pshemos评论)@Keppil好主意!但是我觉得他可以通过修复他的实现来学习线程。@AdamHorvath我编辑了我的问题,你有没有想过这样的想法??这个版本有非阻塞方法:list()立即返回“something”,invalidateCache()不会使用长列表构建操作停止调用程序线程。同时确保只有一个线程可以同时执行loadMyList()。