Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/335.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java缓存对象返回旧值_Java_Synchronization - Fatal编程技术网

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()。