In the following example, I have one file being used by two threads (in the real example I could have any number of threads)
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class A {
static volatile boolean running = true;
public static void main(String[] args) throws IOException, InterruptedException {
String name = "delete.me";
new File(name).deleteOnExit();
RandomAccessFile raf = new RandomAccessFile(name, "rw");
FileChannel fc = raf.getChannel();
Thread monitor = new Thread(() -> {
try {
while (running) {
System.out.println(name + " is " + (fc.size() >> 10) + " KB");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("Interrupted");
Thread.currentThread().interrupt();
}
}
} catch (IOException e) {
System.err.println("Monitor thread died");
e.printStackTrace();
}
});
monitor.setDaemon(true);
monitor.start();
Thread writer = new Thread(() -> {
ByteBuffer bb = ByteBuffer.allocateDirect(32);
try {
while (running) {
bb.position(0).limit(32);
fc.write(bb);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
System.out.println("Interrupted");
Thread.currentThread().interrupt();
}
}
} catch (IOException e) {
System.err.println("Writer thread died");
e.printStackTrace();
}
});
writer.setDaemon(true);
writer.start();
Thread.sleep(5000);
monitor.interrupt();
Thread.sleep(2000);
running = false;
raf.close();
}
}
Rather creating a RandomAccessFile and a memory mapping for each thread, I have one file and one memory mapping shared between threads, but there is a catch, if any thread is interrupted the resource is closed.
delete.me is 0 KB
delete.me is 2 KB
delete.me is 4 KB
delete.me is 6 KB
delete.me is 8 KB
Interrupted
Monitor thread died
java.nio.channels.ClosedByInterruptException
at java.nio.channels.spi.AbstractInterruptibleChannel.end(AbstractInterruptibleChannel.java:202)
at sun.nio.ch.FileChannelImpl.size(FileChannelImpl.java:315)
at A.lambda$main$0(A.java:19)
at java.lang.Thread.run(Thread.java:748)
Writer thread died
java.nio.channels.ClosedChannelException
at sun.nio.ch.FileChannelImpl.ensureOpen(FileChannelImpl.java:110)
at sun.nio.ch.FileChannelImpl.write(FileChannelImpl.java:199)
at A.lambda$main$1(A.java:41)
at java.lang.Thread.run(Thread.java:748)
Is there any way to prevent the FileChannel from being closed just because one thread using it was interrupted?
EDIT What I want to avoid doing is as I suspect it won't work for Java 9+
private void doNotCloseOnInterrupt(FileChannel fc) {
try {
Field field = AbstractInterruptibleChannel.class
.getDeclaredField("interruptor");
field.setAccessible(true);
field.set(fc, (Interruptible) thread
-> Jvm.warn().on(getClass(), fc + " not closed on interrupt"));
} catch (Exception e) {
Jvm.warn().on(getClass(), "Couldn't disable close on interrupt", e);
}
}
BTW The call to fc.size()
returns the size as expected with the above hack.
FileChannel
, there wouldn’t be any problem creating multipleFileChannel
instances on the same file, as this has nothing to do with the virtual memory (as just creating a file channel is not memory mapping). – DaguerreMappedByteBuffer mapped; try(FileChannel fc = FileChannel.open(name, READ, WRITE, CREATE_NEW, DELETE_ON_CLOSE)) { mapped = fc.map(FileChannel.MapMode.READ_WRITE, position, size); } /* use of mapped */
Note that the mapped buffer preventsDELETE_ON_CLOSE
from deleting immediately on close, which turns it into a “delete on exit” behavior, which I found to be more reliable thanFile.deleteOnExit()
… – Daguerre