Java 8陷阱–提防Files.lines()

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值