Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/396.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 有没有办法让FileChannel自动关闭?_Java_Timer_Nio_Filechannel - Fatal编程技术网

Java 有没有办法让FileChannel自动关闭?

Java 有没有办法让FileChannel自动关闭?,java,timer,nio,filechannel,Java,Timer,Nio,Filechannel,我目前正在开发一个应用程序,需要随机访问许多(60k-100k)相对较大的文件。 由于打开和关闭流是一项成本相当高的操作,因此我更愿意将最大文件的文件通道保持打开状态,直到不再需要它们为止 问题在于,由于Java7的try-with语句没有涵盖这种行为,因此我需要手动关闭所有文件通道。 但这变得越来越复杂,因为在整个软件中可以同时访问相同的文件 我实现了一个ChannelPool类,它可以跟踪每个注册路径的已打开FileChannel实例。然后可以发出ChannelPool来关闭那些通道,这些通

我目前正在开发一个应用程序,需要随机访问许多(60k-100k)相对较大的文件。 由于打开和关闭流是一项成本相当高的操作,因此我更愿意将最大文件的文件通道保持打开状态,直到不再需要它们为止

问题在于,由于Java7的try-with语句没有涵盖这种行为,因此我需要手动关闭所有文件通道。 但这变得越来越复杂,因为在整个软件中可以同时访问相同的文件

我实现了一个ChannelPool类,它可以跟踪每个注册路径的已打开FileChannel实例。然后可以发出ChannelPool来关闭那些通道,这些通道的路径在某些时间间隔内仅被池本身弱引用。 我更喜欢事件监听器方法,但我也不希望听GC

来自Apache Commons的消息没有解决我的问题,因为仍然需要手动关闭通道

这个问题有没有更优雅的解决方案?如果没有,如何改进我的实施

import java.io.IOException;
import java.lang.ref.WeakReference;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;

public class ChannelPool {

    private static final ChannelPool defaultInstance = new ChannelPool();

    private final ConcurrentHashMap<String, ChannelRef> channels;
    private final Timer timer;

    private ChannelPool(){
        channels = new ConcurrentHashMap<>();
        timer = new Timer();
    }

    public static ChannelPool getDefault(){
        return defaultInstance;
    }

    public void initCleanUp(){
        // wait 2 seconds then repeat clean-up every 10 seconds.
        timer.schedule(new CleanUpTask(this), 2000, 10000);
    }

    public void shutDown(){
        // must be called manually.
        timer.cancel();
        closeAll();
    }

    public FileChannel getChannel(Path path){
        ChannelRef cref = channels.get(path.toString());
        System.out.println("getChannel called " + channels.size());

        if (cref == null){
            cref = ChannelRef.newInstance(path);
            if (cref == null){
                // failed to open channel
                return null;
            }
            ChannelRef oldRef = channels.putIfAbsent(path.toString(), cref);
            if (oldRef != null){
                try{
                    // close new channel and let GC dispose of it
                    cref.channel().close();
                    System.out.println("redundant channel closed");
                }
                catch (IOException ex) {}
                cref = oldRef;
            }
        }
        return cref.channel();
    }

    private void remove(String str) {   
        ChannelRef ref = channels.remove(str);
        if (ref != null){
            try {
                ref.channel().close();
                System.out.println("old channel closed");
            }
            catch (IOException ex) {}
        }
    }

    private void closeAll() {
        for (Map.Entry<String, ChannelRef> e : channels.entrySet()){
            remove(e.getKey());
        }
    }

    private void maintain() {
        // close channels for derefenced paths
        for (Map.Entry<String, ChannelRef> e : channels.entrySet()){
            ChannelRef ref = e.getValue();
            if (ref != null){
                Path p = ref.pathRef().get();
                if (p == null){
                    // gc'd
                    remove(e.getKey());
                }
            }
        }
    }

    private static class ChannelRef{

        private FileChannel channel;
        private WeakReference<Path> ref;

        private ChannelRef(FileChannel channel, WeakReference<Path> ref) {
            this.channel = channel;
            this.ref = ref;
        }

        private static ChannelRef newInstance(Path path) {
            FileChannel fc;
            try {
                fc = FileChannel.open(path, StandardOpenOption.READ);
            }
            catch (IOException ex) {
                return null;
            }
            return new ChannelRef(fc, new WeakReference<>(path));

        }

        private FileChannel channel() {
            return channel;
        }

        private WeakReference<Path> pathRef() {
            return ref;
        }
    }

    private static class CleanUpTask extends TimerTask {

        private ChannelPool pool;

        private CleanUpTask(ChannelPool pool){
            super();
            this.pool = pool;
        }

        @Override
        public void run() {
            pool.maintain();
            pool.printState();
        }
    }

    private void printState(){
        System.out.println("Clean up performed. " + channels.size() + " channels remain. -- " + System.currentTimeMillis());
        for (Map.Entry<String, ChannelRef> e : channels.entrySet()){
            ChannelRef cref = e.getValue();
            String out = "open: " + cref.channel().isOpen() + " - " + cref.channel().toString();
            System.out.println(out);
        }
    }

}
import java.io.IOException;
导入java.lang.ref.WeakReference;
导入java.nio.channels.FileChannel;
导入java.nio.file.Path;
导入java.nio.file.StandardOpenOption;
导入java.util.Map;
导入java.util.Timer;
导入java.util.TimerTask;
导入java.util.concurrent.ConcurrentHashMap;
公共类频道池{
私有静态最终ChannelPool defaultInstance=新ChannelPool();
专用最终ConcurrentHashMap通道;
私人最终定时器;
专用信道池(){
通道=新的ConcurrentHashMap();
定时器=新定时器();
}
公共静态ChannelPool getDefault(){
返回默认实例;
}
公共void initCleanUp(){
//等待2秒钟,然后每10秒钟重复一次清洁。
时间表(新的清理任务(this),2000,10000);
}
公共空间关闭(){
//必须手动调用。
timer.cancel();
closeAll();
}
公共文件通道getChannel(路径路径){
ChannelRef cref=channels.get(path.toString());
System.out.println(“getChannel调用“+channels.size());
if(cref==null){
cref=ChannelRef.newInstance(路径);
if(cref==null){
//无法打开通道
返回null;
}
ChannelRef oldRef=channels.putIfAbsent(path.toString(),cref);
如果(oldRef!=null){
试一试{
//关闭新通道并让GC处理它
cref.channel().close();
System.out.println(“冗余通道关闭”);
}
catch(IOException ex){}
cref=oldRef;
}
}
返回cref.channel();
}
私有无效删除(字符串str){
ChannelRef=通道。移除(str);
如果(ref!=null){
试一试{
参考通道().close();
System.out.println(“旧通道关闭”);
}
catch(IOException ex){}
}
}
私有void closeAll(){
对于(Map.Entry e:channels.entrySet()){
移除(例如getKey());
}
}
私人文件{
//关闭解除防护路径的通道
对于(Map.Entry e:channels.entrySet()){
ChannelRef=e.getValue();
如果(ref!=null){
路径p=ref.pathRef().get();
if(p==null){
//gc'd
移除(例如getKey());
}
}
}
}
私有静态类ChannelRef{
专用文件通道;
私有WeakReference;
专用信道参考(文件信道信道,WeakReference参考){
this.channel=channel;
this.ref=ref;
}
私有静态ChannelRef newInstance(路径路径){
文件通道fc;
试一试{
fc=FileChannel.open(路径,StandardOpenOption.READ);
}
捕获(IOEX异常){
返回null;
}
返回新的ChannelRef(fc,新的WeakReference(path));
}
专用文件通道通道(){
返回通道;
}
私有WeakReference路径参考(){
返回ref;
}
}
私有静态类清理任务扩展TimerTask{
私人渠道池;
专用清理任务(通道池){
超级();
this.pool=pool;
}
@凌驾
公开募捐{
pool.maintain();
printState();
}
}
私有void printState(){
System.out.println(“已执行清理。”+channels.size()+“保留通道。--”+System.currentTimeMillis());
对于(Map.Entry e:channels.entrySet()){
ChannelRef cref=e.getValue();
String out=“打开:”+cref.channel().isOpen()+“-”+cref.channel().toString();
System.out.println(out);
}
}
}
编辑: 多亏了老师的回答,我现在正好得到了我所需要的。谢谢

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.ExecutionException;

public class Channels {

    private static final LoadingCache<Path, FileChannel> channelCache = 
            CacheBuilder.newBuilder()
            .weakKeys()
            .removalListener(
                new RemovalListener<Path, FileChannel>(){
                    @Override
                    public void onRemoval(RemovalNotification<Path, FileChannel> removal) {
                        FileChannel fc = removal.getValue();
                        try {
                            fc.close();
                        }
                        catch (IOException ex) {}
                    }
                }
            )
            .build(
                new CacheLoader<Path, FileChannel>() {
                    @Override
                    public FileChannel load(Path path) throws IOException {
                        return FileChannel.open(path, StandardOpenOption.READ);
                    }
                }
            );

    public static FileChannel get(Path path){
        try {
            return channelCache.get(path);
        }
        catch (ExecutionException ex){}
        return null;
    }
}
import com.google.common.cache.CacheBuilder;
导入com.google.common.cache.CacheLoader;
导入com.google.common.cache.LoadingCache;
导入com.google.common.cache.RemovalListener;
导入com.google.common.cache.RemovalNotification;
导入java.io.IOException;
导入java.nio.channels.FileChannel;
导入java.nio.file.Path;
导入java.nio.file.StandardOpenOption;
导入java.util.concurrent.ExecutionException;
公共类频道{
专用静态最终加载缓存channelCache=
CacheBuilder.newBuilder()
.weakKeys()
.removalListener(
新RemovalListener(){
@凌驾