Java 有没有办法让FileChannel自动关闭?
我目前正在开发一个应用程序,需要随机访问许多(60k-100k)相对较大的文件。 由于打开和关闭流是一项成本相当高的操作,因此我更愿意将最大文件的文件通道保持打开状态,直到不再需要它们为止 问题在于,由于Java7的try-with语句没有涵盖这种行为,因此我需要手动关闭所有文件通道。 但这变得越来越复杂,因为在整个软件中可以同时访问相同的文件 我实现了一个ChannelPool类,它可以跟踪每个注册路径的已打开FileChannel实例。然后可以发出ChannelPool来关闭那些通道,这些通道的路径在某些时间间隔内仅被池本身弱引用。 我更喜欢事件监听器方法,但我也不希望听GC 来自Apache Commons的消息没有解决我的问题,因为仍然需要手动关闭通道 这个问题有没有更优雅的解决方案?如果没有,如何改进我的实施Java 有没有办法让FileChannel自动关闭?,java,timer,nio,filechannel,Java,Timer,Nio,Filechannel,我目前正在开发一个应用程序,需要随机访问许多(60k-100k)相对较大的文件。 由于打开和关闭流是一项成本相当高的操作,因此我更愿意将最大文件的文件通道保持打开状态,直到不再需要它们为止 问题在于,由于Java7的try-with语句没有涵盖这种行为,因此我需要手动关闭所有文件通道。 但这变得越来越复杂,因为在整个软件中可以同时访问相同的文件 我实现了一个ChannelPool类,它可以跟踪每个注册路径的已打开FileChannel实例。然后可以发出ChannelPool来关闭那些通道,这些通
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(){
@凌驾