有没有办法排队异步I / O操作?

时间:2013-01-19 01:03:23

标签: java asynchronous queue nio

具体来说,我正在寻找的行为是这样的: 读操作同时发生,并将在所有挂起的写操作完成后执行。 写操作始终等待所有其他读/写操作完成。 关闭操作始终等待,直到所有其他读/写操作完成。 换句话说,这些操作应该排队。

NIO FileLocks的官方文档未指定此行为。事实上,它表明:

  

代表整个Java虚拟机保存文件锁。他们   不适合控制多个线程对文件的访问   在同一个虚拟机中。

在提交新的I / O请求之前,我已经尝试过手动排队所有请求并在所有未完成的期货上调用get() 但我不知道这是不是一个好主意。

我该如何实现这种行为?

编辑:感谢fge的见解,我设法找到了解决问题的基本方法:

import java.nio.file.Path;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ChannelAccessFactory {

    public static final ExecutorService IO_THREADS = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    private final Path file;
    private final ReadWriteLock lock;

    public ChannelAccessFactory (Path file){
        this.file = file;
        this.lock = new ReentrantReadWriteLock();
    }

    public ReadWriteLock getLock(){
        return lock;
    }

    public ChannelAccess newAccess() throws Exception{
        return new ChannelAccess(file, lock);
    }

}

Wrapped Channel类:

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.locks.ReadWriteLock;

public class ChannelAccess implements AutoCloseable{

    private final ReadWriteLock lock;
    private final AsynchronousFileChannel channel;

    protected ChannelAccess (Path file, ReadWriteLock lock) throws Exception{
        this.lock = lock;
        this.channel = AsynchronousFileChannel.open(file, StandardOpenOption.READ, StandardOpenOption.WRITE);
    }

    public Future<Integer> read(final ByteBuffer buffer, final long position){
        return ChannelAccessFactory.IO_THREADS.submit(new Callable<Integer>(){
            @Override
            public Integer call() throws InterruptedException, ExecutionException{
                lock.readLock().lock();
                try{
                    return channel.read(buffer, position).get();
                }
                finally {
                    lock.readLock().unlock();
                }
            }
        });
    }

    public Future<Integer> write(final ByteBuffer buffer, final long position){
        return ChannelAccessFactory.IO_THREADS.submit(new Callable<Integer>(){
            @Override
            public Integer call() throws InterruptedException, ExecutionException {
                lock.writeLock().lock();
                try{
                    return channel.write(buffer, position).get();
                }
                finally {
                    lock.writeLock().unlock();
                }
            }
        });
    }

    public long size() throws Exception{
        lock.readLock().lock();
        try{
            return channel.size();
        }
        finally{
            lock.readLock().unlock();
        }
    }

    @Override
    public void close() {
        lock.readLock().lock();
        try{
            channel.close();
        }
        catch (IOException e){}
        finally{
            lock.readLock().unlock();
        }
    }
}

1 个答案:

答案 0 :(得分:3)

使用ReentrantReadWriteLock。它允许许多并发读者,但只允许一个作者。

它不是一个“纯粹的”NIO解决方案,但它是一个原始的行为,你的行为方式。

小心:使用这样的锁来避免死锁:

rwlock.readLock().lock();
try {
    // do stuff
} finally {
    rwlock.readLock().unlock();
}