在不关闭的情况下使流无效
Invalidate Stream without Closing
这是
的后续
根据我之前的问题,我不能依赖这样的代码(现在恰好在 JDK8 中工作):
RandomAccessFile r = new RandomAccessFile(...);
FileInputStream f_1 = new FileInputStream(r.getFD());
// some io, not shown
f_1 = null;
f_2 = new FileInputStream(r.getFD());
// some io, not shown
f_2 = null;
f_3 = new FileInputStream(r.getFD());
// some io, not shown
f_3 = null;
但是,为了防止意外错误并作为一种自我记录的形式,我想在使用完每个文件流后使它失效——而不关闭底层文件描述符。
每个 FileInputStream 都是独立的,其定位由 RandomAccessFile 控制。我共享相同的 FileDescriptor 以防止因多次打开相同路径而引起的任何竞争条件。当我用完一个 FileInputStream 时,我想使它无效,以便在使用第二个 FileInputStream 时不会意外读取它(这会导致第二个 FileInputStream 跳过数据)。
我该怎么做?
备注:
- 我使用的库需要与 java.io 兼容。*
- 如果你建议一个库(如果可能的话,我更喜欢内置 java 语义),它必须是 linux(主要目标)的普遍可用(打包)可用于 windows(实验目标)
- 但是,windows 支持并不是绝对必需的
编辑:回复评论,这是我的工作流程:
RandomAccessFile r = new RandomAccessFile(String path, "r");
int header_read;
int header_remaining = 4; // header length, initially
byte[] ba = new byte[header_remaining];
ByteBuffer bb = new ByteBuffer.allocate(header_remaining);
while ((header_read = r.read(ba, 0, header_remaining) > 0) {
header_remaining -= header_read;
bb.put(ba, 0, header_read);
}
byte[] header = bb.array();
// process header, not shown
// the RandomAccessFile above reads only a small amount, so buffering isn't required
r.seek(0);
FileInputStream f_1 = new FileInputStream(r.getFD());
Library1Result result1 = library1.Main.entry_point(f_1)
// process result1, not shown
// Library1 reads the InputStream in large chunks, so buffering isn't required
// invalidate f_1 (this question)
r.seek(0)
int read;
while ((read = r.read(byte[4096] buffer)) > 0 && library1.continue()) {
library2.process(buffer, read);
}
// the RandomAccessFile above is read in large chunks, so buffering isn't required
// in a previous edit the RandomAccessFile was used to create a FileInputStream. Obviously that's not required, so ignore
r.seek(0)
Reader r_1 = new BufferedReader(new InputStreamReader(new FileInputStream(r.getFD())));
Library3Result result3 = library3.Main.entry_point(r_2)
// process result3, not shown
// I'm not sure how Library3 uses the reader, so I'm providing buffering
// invalidate r_1 (this question) - bonus: frees the buffer
r.seek(0);
FileInputStream f_2 = new FileInputStream(r.getFD());
Library1Result result1 = library1.Main.entry_point(f_2)
// process result1 (reassigned), not shown
// Yes, I actually have to call 'library1.Main.entry_point' *again* - same comments apply as from before
// invalidate f_2 (this question)
//
// I've been told to be careful when opening multiple streams from the same
// descriptor if one is buffered. This is very vague. I assume because I only
// ever use any stream once and exclusively, this code is safe.
//
在 unix 下,您通常可以通过 dup 文件描述符来避免此类问题。
由于 java 不提供这样的功能,一个选项是公开该功能的本机库。 jnr-posix 例如这样做。另一方面,jnr 比您原来的问题更依赖 jdk 实现属性。
一个纯粹的 Java 解决方案可能是创建一个转发装饰器来检查每个方法调用是否验证了流。对于 InputStream
,此装饰器可能如下所示:
public final class CheckedInputStream extends InputStream {
final InputStream delegate;
boolean validated;
public CheckedInputStream(InputStream stream) throws FileNotFoundException {
delegate = stream;
validated = true;
}
public void invalidate() {
validated = false;
}
void checkValidated() {
if (!validated) {
throw new IllegalStateException("Stream is invalidated.");
}
}
@Override
public int read() throws IOException {
checkValidated();
return delegate.read();
}
@Override
public int read(byte b[]) throws IOException {
checkValidated();
return read(b, 0, b.length);
}
@Override
public int read(byte b[], int off, int len) throws IOException {
checkValidated();
return delegate.read(b, off, len);
}
@Override
public long skip(long n) throws IOException {
checkValidated();
return delegate.skip(n);
}
@Override
public int available() throws IOException {
checkValidated();
return delegate.available();
}
@Override
public void close() throws IOException {
checkValidated();
delegate.close();
}
@Override
public synchronized void mark(int readlimit) {
checkValidated();
delegate.mark(readlimit);
}
@Override
public synchronized void reset() throws IOException {
checkValidated();
delegate.reset();
}
@Override
public boolean markSupported() {
checkValidated();
return delegate.markSupported();
}
}
您可以像这样使用它:
CheckedInputStream f_1 = new CheckedInputStream(new FileInputStream(r.getFD()));
// some io, not shown
f_1.invalidate();
f_1.read(); // throws IllegalStateException
这是
根据我之前的问题,我不能依赖这样的代码(现在恰好在 JDK8 中工作):
RandomAccessFile r = new RandomAccessFile(...);
FileInputStream f_1 = new FileInputStream(r.getFD());
// some io, not shown
f_1 = null;
f_2 = new FileInputStream(r.getFD());
// some io, not shown
f_2 = null;
f_3 = new FileInputStream(r.getFD());
// some io, not shown
f_3 = null;
但是,为了防止意外错误并作为一种自我记录的形式,我想在使用完每个文件流后使它失效——而不关闭底层文件描述符。
每个 FileInputStream 都是独立的,其定位由 RandomAccessFile 控制。我共享相同的 FileDescriptor 以防止因多次打开相同路径而引起的任何竞争条件。当我用完一个 FileInputStream 时,我想使它无效,以便在使用第二个 FileInputStream 时不会意外读取它(这会导致第二个 FileInputStream 跳过数据)。
我该怎么做?
备注:
- 我使用的库需要与 java.io 兼容。*
- 如果你建议一个库(如果可能的话,我更喜欢内置 java 语义),它必须是 linux(主要目标)的普遍可用(打包)可用于 windows(实验目标)
- 但是,windows 支持并不是绝对必需的
编辑:回复评论,这是我的工作流程:
RandomAccessFile r = new RandomAccessFile(String path, "r");
int header_read;
int header_remaining = 4; // header length, initially
byte[] ba = new byte[header_remaining];
ByteBuffer bb = new ByteBuffer.allocate(header_remaining);
while ((header_read = r.read(ba, 0, header_remaining) > 0) {
header_remaining -= header_read;
bb.put(ba, 0, header_read);
}
byte[] header = bb.array();
// process header, not shown
// the RandomAccessFile above reads only a small amount, so buffering isn't required
r.seek(0);
FileInputStream f_1 = new FileInputStream(r.getFD());
Library1Result result1 = library1.Main.entry_point(f_1)
// process result1, not shown
// Library1 reads the InputStream in large chunks, so buffering isn't required
// invalidate f_1 (this question)
r.seek(0)
int read;
while ((read = r.read(byte[4096] buffer)) > 0 && library1.continue()) {
library2.process(buffer, read);
}
// the RandomAccessFile above is read in large chunks, so buffering isn't required
// in a previous edit the RandomAccessFile was used to create a FileInputStream. Obviously that's not required, so ignore
r.seek(0)
Reader r_1 = new BufferedReader(new InputStreamReader(new FileInputStream(r.getFD())));
Library3Result result3 = library3.Main.entry_point(r_2)
// process result3, not shown
// I'm not sure how Library3 uses the reader, so I'm providing buffering
// invalidate r_1 (this question) - bonus: frees the buffer
r.seek(0);
FileInputStream f_2 = new FileInputStream(r.getFD());
Library1Result result1 = library1.Main.entry_point(f_2)
// process result1 (reassigned), not shown
// Yes, I actually have to call 'library1.Main.entry_point' *again* - same comments apply as from before
// invalidate f_2 (this question)
//
// I've been told to be careful when opening multiple streams from the same
// descriptor if one is buffered. This is very vague. I assume because I only
// ever use any stream once and exclusively, this code is safe.
//
在 unix 下,您通常可以通过 dup 文件描述符来避免此类问题。
由于 java 不提供这样的功能,一个选项是公开该功能的本机库。 jnr-posix 例如这样做。另一方面,jnr 比您原来的问题更依赖 jdk 实现属性。
一个纯粹的 Java 解决方案可能是创建一个转发装饰器来检查每个方法调用是否验证了流。对于 InputStream
,此装饰器可能如下所示:
public final class CheckedInputStream extends InputStream {
final InputStream delegate;
boolean validated;
public CheckedInputStream(InputStream stream) throws FileNotFoundException {
delegate = stream;
validated = true;
}
public void invalidate() {
validated = false;
}
void checkValidated() {
if (!validated) {
throw new IllegalStateException("Stream is invalidated.");
}
}
@Override
public int read() throws IOException {
checkValidated();
return delegate.read();
}
@Override
public int read(byte b[]) throws IOException {
checkValidated();
return read(b, 0, b.length);
}
@Override
public int read(byte b[], int off, int len) throws IOException {
checkValidated();
return delegate.read(b, off, len);
}
@Override
public long skip(long n) throws IOException {
checkValidated();
return delegate.skip(n);
}
@Override
public int available() throws IOException {
checkValidated();
return delegate.available();
}
@Override
public void close() throws IOException {
checkValidated();
delegate.close();
}
@Override
public synchronized void mark(int readlimit) {
checkValidated();
delegate.mark(readlimit);
}
@Override
public synchronized void reset() throws IOException {
checkValidated();
delegate.reset();
}
@Override
public boolean markSupported() {
checkValidated();
return delegate.markSupported();
}
}
您可以像这样使用它:
CheckedInputStream f_1 = new CheckedInputStream(new FileInputStream(r.getFD()));
// some io, not shown
f_1.invalidate();
f_1.read(); // throws IllegalStateException