Java8中有一个非常不错的新功能,它允许您在一个衬里中从文件中获取字符串流。
List lines = Files.lines(path).collect(Collectors.toList());
您可以像对待任何其他Stream一样操作Stream,例如,您可能想要filter()或map()或limit()或skip()等。我开始在整个代码中使用它,直到被这个异常击中,
Caused by: java.nio.file.FileSystemException: /tmp/date.txt: Too many open files in system
at sun.nio.fs.UnixException.translateToIOException(UnixException.java:91)
at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:102)
at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:107)
at sun.nio.fs.UnixFileSystemProvider.newByteChannel(UnixFileSystemProvider.java:214)
at java.nio.file.Files.newByteChannel(Files.java:361)
at java.nio.file.Files.newByteChannel(Files.java:407)
at java.nio.file.spi.FileSystemProvider.newInputStream(FileSystemProvider.java:384)
at java.nio.file.Files.newInputStream(Files.java:152)
at java.nio.file.Files.newBufferedReader(Files.java:2784)
at java.nio.file.Files.lines(Files.java:3744)
at java.nio.file.Files.lines(Files.java:3785)
由于某些原因,我打开的文件太多了! 奇怪,Files.lines()不会关闭文件吗?
请参阅下面的代码( run3()
),其中创建了该代码,从而重现了该问题:
package utility;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Date;
import java.util.stream.Stream;
public class Test2 {
public static void main(String[] args) throws IOException{
int times = 100_000;
Path path = Paths.get("/tmp", "date.txt");
Test2 t2 = new Test2();
t2.setDate(path);
for (int i = 0; i < times; i++) {
t2.run1(path);
}
for (int i = 0; i < times; i++) {
t2.run2(path);
}
for (int i = 0; i < times; i++) {
t2.run3(path); //throws exception too many files open
}
System.out.println("finished");
}
public String run1(Path path){
try(BufferedReader br = new BufferedReader(new FileReader(path.toFile()))){
return br.readLine();
} catch (IOException e) {
throw new AssertionError(e);
}
}
public String run2(Path path){
try(Stream<String> stream = Files.lines(path)) {
return stream.findFirst().get();
} catch (IOException e) {
throw new AssertionError(e);
}
}
public String run3(Path path) throws IOException{
return Files.lines(path).findFirst().get();
}
public void setDate(Path path) {
try (FileWriter writer = new FileWriter(path.toFile())){
writer.write(new Date().toString());
writer.flush();
} catch (IOException e) {
throw new AssertionError(e);
}
}
}
我的代码看起来像run3()
,它产生了异常。 我通过运行unix命令lsof
(列出打开的文件)并注意到许多date.txt实例打开来证明这一点。 要检查这个问题确实与Files.lines()
我确信,代码跑了run1()
使用BufferedReader
,它做到了。 通过阅读Files
的源代码,我意识到Stream需要以可自动关闭的方式创建 。 当我在run2()
该代码时,代码再次正常运行。
我认为这并不是特别直观。 当您必须使用自动关闭装置时,它确实损坏了一个衬管。 我想代码确实需要有关何时关闭文件的信号,但是以某种方式隐藏起来对我们来说会很好。 至少应在JavaDoc中突出显示它,而不是:-)
翻译自: https://www.javacodegeeks.com/2015/02/java-8-pitfall-beware-files-lines.html