Java:异步将对象序列化为文件

Java:异步将对象序列化为文件,java,performance,asynchronous,serialization,completable-future,Java,Performance,Asynchronous,Serialization,Completable Future,我正在制作一个Minecraft克隆,我有一个名为chunks的Map存储所有加载的chunks。每帧,此循环运行: for(矢量3i v:chunkstounender){ ... CompletableFuture.runAsync(()->{ 试一试{ chunks.get(v.toFile)(新文件(String.format(“res-server/regions/%s.%s.mcr”,v.x,v.y,v.z)); 已同步(chunksStateGuard){ 块。移除(v); } }

我正在制作一个Minecraft克隆,我有一个名为
chunks
Map
存储所有加载的chunks。每帧,此循环运行:

for(矢量3i v:chunkstounender){
...
CompletableFuture.runAsync(()->{
试一试{
chunks.get(v.toFile)(新文件(String.format(“res-server/regions/%s.%s.mcr”,v.x,v.y,v.z));
已同步(chunksStateGuard){
块。移除(v);
}
}捕获(IOE异常){
e、 printStackTrace();
System.err.println(“无法保存区块”+Utils.format3i(v));
}
});
}
这里的目标是异步卸载块。
Chunk.toFile(文件)
的内容是:

public void toFile(文件)引发IOException{
FileOutputStream fos=新的FileOutputStream(文件);
fos.write(SerializationUtils.serialize(this));
fos.flush();
fos.close();
}

然而,尽管使用了
CompletableFuture
,每当卸载块时,游戏会在短时间内以帧率命中,因为它会序列化并卸载块。有没有办法避免在后台任务工作时中断主线程?

这是一个X-Y问题。问题是从文件中同步加载块。

正如前面的评论中所讨论的,您可以使用缓存进一步增强实现,该缓存优化序列化过程,同时提供更快的读取

下面是一个示例代码,它演示了如何使用ehCache设置和使用缓存

Maven依赖项(在pom.xml中)


net.sf.ehcache
ehcache
2.9.0
ehCache配置(ehCache.xml)

注意-此文件应在类路径中可用

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"
    monitoring="autodetect" dynamicConfig="true">

    <!-- By default, Ehcache stored the cached files in temp folder. -->
    <!-- <diskStore path="java.io.tmpdir" /> -->

    <!-- Ask Ehcache to store cache in this path -->
    <diskStore path="c:\\temp\\ehcache" />

    <!-- Sample cache named cache1
    This cache contains a maximum in memory of 10000 elements, and will expire
    an element if it is idle for more than 5 minutes and lives for more than
    10 minutes.

    If there are more than 10000 elements it will overflow to the
    disk cache, which in this configuration will go to wherever java.io.tmp is
    defined on your system. On a standard Linux system this will be /tmp" -->
    <cache name="ApplicationContentCache" 
        maxEntriesLocalHeap="10000"
        maxEntriesLocalDisk="1000" 
        eternal="false" 
        diskSpoolBufferSizeMB="20"
        timeToIdleSeconds="300" timeToLiveSeconds="600"
        memoryStoreEvictionPolicy="LFU" 
        transactionalMode="off">
        <persistence strategy="localTempSwap" />
    </cache>
</ehcache>

各种Java类

import java.io.Serializable;
import java.util.concurrent.CompletableFuture;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;

/**
 * Demo class for the caching functionality
 * @author Rahul R
 *
 */
public class ApplicationCacheDemo {

    public static void main(String... args) {
        ApplicationCacheFactory cacheFactory = ApplicationCacheFactory.instance;

        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            try (ApplicationCache<CachableObject> cache = cacheFactory.getApplicationCache()) {
                CachableObject cacheContent = new CachableObject("A sample content");
                int identity = cache.put(cacheContent);

                CachableObject readContent = cache.get(identity);
                System.out.println(readContent.getData());          
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        future.join();
    }
}

/**
 * The class whose objects would be cached.
 * @author Rahul R
 *
 */

class CachableObject implements Serializable {
    private static final long serialVersionUID = 1L;

    private String data = null;

    public CachableObject() {
        super();
    }

    public CachableObject(String data) {
        super();
        setData(data);
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((data == null) ? 0 : data.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        CachableObject other = (CachableObject) obj;
        if (data == null) {
            if (other.data != null)
                return false;
        } else if (!data.equals(other.data))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return "CachableObject [" + getData() + "]";
    }
}

/**
 * A singleton factory implemented through Enum
 * @author Rahul R
 *
 */

enum ApplicationCacheFactory {
    instance;

    public ApplicationCache<CachableObject> getApplicationCache() {
        return new ApplicationCache<>("ApplicationContentCache");
    }
}

/**
 * A simplistic cache facade
 * @author Rahul R
 *
 */

class ApplicationCache<E> implements AutoCloseable {    
    private CacheManager cm = null;
    private String cacheName = null;
    private Cache cache = null;

    public ApplicationCache(String cacheName) {
        super();
        setCacheName(cacheName);
        initializeCache();
    }

    private void initializeCache() {
        cm = CacheManager.getInstance();
        cache = cm.getCache(getCacheName());
    }

    public String getCacheName() {
        return cacheName;
    }

    public void setCacheName(String cacheName) {
        this.cacheName = cacheName;
    }

    public int put(E value) {
        int identity = value.hashCode();
        cache.put(new Element(identity, value));
        return identity;
    }

    @SuppressWarnings("unchecked")
    public E get(int identity) {
        E result = null;

        Element element = cache.get(identity);

        if (element != null) {
            result = (E) element.getObjectValue();
        }

        return result;
    }

    @Override
    public void close() throws Exception {
        cm.shutdown();
    }
}
import java.io.Serializable;
导入java.util.concurrent.CompletableFuture;
导入net.sf.ehcache.Cache;
导入net.sf.ehcache.CacheManager;
导入net.sf.ehcache.Element;
/**
*用于缓存功能的演示类
*@作者Rahul R
*
*/
公共类应用程序CacheDemo{
公共静态void main(字符串…参数){
ApplicationCacheFactory cacheFactory=ApplicationCacheFactory.instance;
CompletableFuture=CompletableFuture.runAsync(()->{
try(ApplicationCache=cacheFactory.getApplicationCache()){
CacableObject cacheContent=新的CacableObject(“示例内容”);
int identity=cache.put(cacheContent);
cacableobject readContent=cache.get(标识);
System.out.println(readContent.getData());
}捕获(例外e){
e、 printStackTrace();
}
});
future.join();
}
}
/**
*将缓存其对象的类。
*@作者Rahul R
*
*/
类cacableobject实现可序列化{
私有静态最终长serialVersionUID=1L;
私有字符串数据=null;
公共可缓存对象(){
超级();
}
公共可缓存对象(字符串数据){
超级();
setData(数据);
}
公共字符串getData(){
返回数据;
}
公共void setData(字符串数据){
这个数据=数据;
}
@凌驾
公共int hashCode(){
最终整数素数=31;
int结果=1;
result=prime*result+((数据==null)?0:data.hashCode();
返回结果;
}
@凌驾
公共布尔等于(对象obj){
if(this==obj)
返回true;
if(obj==null)
返回false;
如果(getClass()!=obj.getClass())
返回false;
CACABLABLE OBJECT other=(CACABLABLE OBJECT)obj;
如果(数据==null){
if(other.data!=null)
返回false;
}如果(!data.equals(other.data))
返回false;
返回true;
}
@凌驾
公共字符串toString(){
返回“cacableobject[”+getData()+“]”;
}
}
/**
*通过枚举实现的单例工厂
*@作者Rahul R
*
*/
枚举应用程序缓存工厂{
实例;
公共应用程序缓存getApplicationCache(){
返回新的ApplicationCache(“ApplicationContentCache”);
}
}
/**
*简单化的缓存外观
*@作者Rahul R
*
*/
类ApplicationCache实现自动关闭{
私有缓存管理器cm=null;
私有字符串cacheName=null;
私有缓存=空;
公共应用程序缓存(字符串缓存名称){
超级();
setCacheName(cacheName);
initializeCache();
}
private void initializeCache(){
cm=CacheManager.getInstance();
cache=cm.getCache(getCacheName());
}
公共字符串getCacheName(){
返回cacheName;
}
public void setCacheName(字符串cacheName){
this.cacheName=cacheName;
}
公共整数输出(E值){
int identity=value.hashCode();
put(新元素(标识、值));
返回身份;
}
@抑制警告(“未选中”)
公共E-get(int-identity){
E结果=空;
Element=cache.get(标识);
if(元素!=null){
结果=(E)element.getObjectValue();
}
返回结果;
}
@凌驾
public void close()引发异常{
cm.关机();
}
}

文件IO成本高昂,您是否考虑过将频繁使用的内容序列化到内存缓存(如ehCache或Terracota)?@rahull。序列化的全部目的是减少内存消耗。起初,所有的区块都存储在内存中,但在大约100万次的游戏中,我们会出现滞后峰值和OutOfMemoryErrors。@Rahull。我不明白你的意思。如果你的建议能解决问题,
import java.io.Serializable;
import java.util.concurrent.CompletableFuture;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;

/**
 * Demo class for the caching functionality
 * @author Rahul R
 *
 */
public class ApplicationCacheDemo {

    public static void main(String... args) {
        ApplicationCacheFactory cacheFactory = ApplicationCacheFactory.instance;

        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            try (ApplicationCache<CachableObject> cache = cacheFactory.getApplicationCache()) {
                CachableObject cacheContent = new CachableObject("A sample content");
                int identity = cache.put(cacheContent);

                CachableObject readContent = cache.get(identity);
                System.out.println(readContent.getData());          
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        future.join();
    }
}

/**
 * The class whose objects would be cached.
 * @author Rahul R
 *
 */

class CachableObject implements Serializable {
    private static final long serialVersionUID = 1L;

    private String data = null;

    public CachableObject() {
        super();
    }

    public CachableObject(String data) {
        super();
        setData(data);
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((data == null) ? 0 : data.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        CachableObject other = (CachableObject) obj;
        if (data == null) {
            if (other.data != null)
                return false;
        } else if (!data.equals(other.data))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return "CachableObject [" + getData() + "]";
    }
}

/**
 * A singleton factory implemented through Enum
 * @author Rahul R
 *
 */

enum ApplicationCacheFactory {
    instance;

    public ApplicationCache<CachableObject> getApplicationCache() {
        return new ApplicationCache<>("ApplicationContentCache");
    }
}

/**
 * A simplistic cache facade
 * @author Rahul R
 *
 */

class ApplicationCache<E> implements AutoCloseable {    
    private CacheManager cm = null;
    private String cacheName = null;
    private Cache cache = null;

    public ApplicationCache(String cacheName) {
        super();
        setCacheName(cacheName);
        initializeCache();
    }

    private void initializeCache() {
        cm = CacheManager.getInstance();
        cache = cm.getCache(getCacheName());
    }

    public String getCacheName() {
        return cacheName;
    }

    public void setCacheName(String cacheName) {
        this.cacheName = cacheName;
    }

    public int put(E value) {
        int identity = value.hashCode();
        cache.put(new Element(identity, value));
        return identity;
    }

    @SuppressWarnings("unchecked")
    public E get(int identity) {
        E result = null;

        Element element = cache.get(identity);

        if (element != null) {
            result = (E) element.getObjectValue();
        }

        return result;
    }

    @Override
    public void close() throws Exception {
        cm.shutdown();
    }
}