FileInputStream和Files.newInputStream?

前言

在上传文件的时候,发现还有一个新的东西叫做Files.newInputStream,就稍微看了一下下。主要应用示例在这篇文章中。

FileInputStream

很久很久以前就开始用这个库了,是一个文件流,可以读取文件。

主要用法也是直接新建:

1
FileInputStream fis = new FileInputStream("filePath");

或者

1
FileInputStream fis = new FileInputStream(new File("filePath"));

在源码中这两个的效果也是一样的。

使用完毕后,关闭:

1
fis.close();

或者你想稳妥一点:

1
fis.finalize();

这个库主要就是以只读的方式打开文件,读取文件,最后处理。就不再多说了。网上研究这个的人比我吃的饭都多。

Files.newInputStream

这个库是java.nio.file.Files中的方法,是FileInputStream的升级版。

我们来看看这个玩意的源码是什么意思:

1
2
3
public static InputStream newInputStream(Path path, OpenOption... options) throws IOException {
return provider(path).newInputStream(path, options);
}

通过自己的静态方法provider,获取到FileSystemProvider,然后调用newInputStream方法。

这个FileSystemProvider是一个抽象类,也提供了一个newInputStream方法:

1
2
3
4
5
6
7
8
9
10
11
public InputStream newInputStream(Path path, OpenOption... options) throws IOException {
if (options.length > 0) {
for (OpenOption opt: options) {
// All OpenOption values except for APPEND and WRITE are allowed
if (opt == StandardOpenOption.APPEND ||
opt == StandardOpenOption.WRITE)
throw new UnsupportedOperationException("'" + opt + "' not allowed");
}
}
return Channels.newInputStream(Files.newByteChannel(path, options));
}

在这里,将通过Channels类的newInputStream方法给出输入流。

为什么偏偏要这么做呢?

最后看到这个方法是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Constructs a stream that reads bytes from the given channel.
*
* <p> The <tt>read</tt> methods of the resulting stream will throw an
* {@link IllegalBlockingModeException} if invoked while the underlying
* channel is in non-blocking mode. The stream will not be buffered, and
* it will not support the {@link InputStream#mark mark} or {@link
* InputStream#reset reset} methods. The stream will be safe for access by
* multiple concurrent threads. Closing the stream will in turn cause the
* channel to be closed. </p>
*
* @param ch
* The channel from which bytes will be read
*
* @return A new input stream
*/
public static InputStream newInputStream(ReadableByteChannel ch) {
checkNotNull(ch, "ch");
return new sun.nio.ch.ChannelInputStream(ch);
}

不难看出,这个方法的优势正如注释所述,是线程安全的。

为什么偏偏他是线程安全的?

这就是sun.nio.chChannelInputStream类的作用了。这个类以ReadableByteChannel作为构造函数。ReadableByteChannel类是这样注释的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* A channel that can read bytes.
*
* <p> Only one read operation upon a readable channel may be in progress at
* any given time. If one thread initiates a read operation upon a channel
* then any other thread that attempts to initiate another read operation will
* block until the first operation is complete. Whether or not other kinds of
* I/O operations may proceed concurrently with a read operation depends upon
* the type of the channel. </p>
*
*
* @author Mark Reinhold
* @author JSR-51 Expert Group
* @since 1.4
*/

public interface ReadableByteChannel extends Channel {}

这就是线程安全的非阻塞IO的核心了。

所以,不难看出,这个类最核心的地方就是将IO模式转变为非阻塞的NIO模式,这也顺应了现阶段高吞吐量或处理大文件的应用场景,趁着CPU把任务丢给磁盘的时候,趁着闲下来的时间做点别的事,提升处理效率。

而这一点,也使得Files.newInputStream能够进一步与其他的NIO操作集成,从而在处理大文件的时候,充分利用缓冲区的优势,处理起来也比FileInputStream要快很多。