当前位置: 首页 > 知识库问答 >
问题:

关闭管道中间的水流

孟福
2023-03-14

当我执行这段代码时,它会在流管道中打开许多文件:

public static void main(String[] args) throws IOException {
    Files.find(Paths.get("JAVA_DOCS_DIR/docs/api/"),
            100, (path, attr) -> path.toString().endsWith(".html"))
        .map(file -> runtimizeException(() -> Files.lines(file, StandardCharsets.ISO_8859_1)))
        .map(Stream::count)
        .forEachOrdered(System.out::println);
}

我得到一个例外:

java.nio.file.FileSystemException: /long/file/name: Too many open files

问题是流。当完成对流的遍历时,count不关闭流。但我不明白为什么不应该,因为这是一个终端操作。对于其他终端操作,如reduceforEach,也是如此<另一方面,代码>平面图关闭它所包含的流。

文档告诉我在必要时使用try with resources语句来关闭流。在我的例子中,我可以用以下内容替换计数行:

.map(s -> { long c = s.count(); s.close(); return c; } )

但这是嘈杂和丑陋的,在某些情况下,对于大型复杂的管道来说,可能会带来真正的不便。

我的问题如下:

  1. 为什么流的设计不是为了让终端操作关闭它们正在处理的流?这将使它们更好地处理IO流

RuntimeException是一种将选中的异常包装在RuntimeException中的方法。


共有3个答案

萧无尘
2023-03-14

您将需要在此流操作中调用lock(),这将导致调用所有底层的关闭处理程序。

更好的是,将您的整个语句包装在一个try with resources块中,因为这样它将自动调用close处理程序。

这在您的情况下可能不太可能,这意味着您需要在某些操作中自行处理。您当前的方法可能根本不适合流。

看起来您确实需要在第二次map()操作中执行此操作。

安承教
2023-03-14

可以创建一个实用程序方法,可靠地关闭管道中间的流。

这确保了每个资源都用try with resource语句关闭,但避免了自定义实用程序方法的需要,并且比直接在lambda中编写try语句要详细得多。

使用此方法,问题中的管道如下所示:

Files.find(Paths.get("Java_8_API_docs/docs/api"), 100,
        (path, attr) -> path.toString().endsWith(".html"))
    .map(file -> applyAndClose(
        () -> Files.lines(file, StandardCharsets.ISO_8859_1),
        Stream::count))
    .forEachOrdered(System.out::println);

实现如下所示:

/**
 * Applies a function to a resource and closes it afterwards.
 * @param sup Supplier of the resource that should be closed
 * @param op operation that should be performed on the resource before it is closed
 * @return The result of calling op.apply on the resource 
 */
private static <A extends AutoCloseable, B> B applyAndClose(Callable<A> sup, Function<A, B> op) {
    try (A res = sup.call()) {
        return op.apply(res);
    } catch (RuntimeException exc) {
        throw exc;
    } catch (Exception exc) {
        throw new RuntimeException("Wrapped in applyAndClose", exc);
    }
}

(由于需要关闭的资源在分配时通常也会抛出异常,非运行时异常被包装在运行时异常中,因此不需要单独的方法来完成此操作。)

汪成仁
2023-03-14

这里有两个问题:处理已检查的异常,例如IOException,以及及时关闭资源。

没有一个预定义的功能接口声明任何选中的异常,这意味着它们必须在lambda中处理,或者包装在未选中的异常中并重新调用。看起来您的函数runtimizeException就是这样做的。您可能还必须为其声明自己的功能接口。正如你可能已经发现的那样,这是一种痛苦。

在关闭像文件这样的资源时,有人研究过在流结束时自动关闭流。这会很方便,但它不处理抛出异常时的关闭。在流中没有神奇的做正确的事情机制。

我们只剩下处理资源闭包的标准Java技术,即Java 7中引入的try-with-resources构造。TWR真的希望资源在调用堆栈中与打开时处于同一级别。适用“谁打开它,谁就必须关闭它”的原则。TWR还处理异常处理,这通常使在同一位置处理异常处理和资源关闭变得方便。

在这个例子中,流有点不寻常,因为它映射了一个Stream

构建该管道的另一种方法如下。

Files.lines调用是打开文件的调用,所以这必须是TWR语句中的资源。这个文件的处理是抛出(一些)IOExceptions的地方,所以我们可以在同一个TWR语句中进行异常包装。这建议有一个简单的函数,将路径映射到行数,同时处理资源关闭和异常包装:

long lineCount(Path path) {
    try (Stream<String> s = Files.lines(path, StandardCharsets.ISO_8859_1)) {
        return s.count();
    } catch (IOException ioe) {
        throw new UncheckedIOException(ioe);
    }
}

一旦有了这个helper函数,主管道如下所示:

Files.find(Paths.get("JAVA_DOCS_DIR/docs/api/"),
           100, (path, attr) -> path.toString().endsWith(".html"))
     .mapToLong(this::lineCount)
     .forEachOrdered(System.out::println);

 类似资料:
  • 获取频道报表 获取频道资金流水 获取频道打赏流水 获取频道发红包/抢红包记录 获取频道付费流水 获取观众观看流水_V2 获取观众观看流水_V1 获取频道报名问卷数据 获取频道问卷列表数据 获取频道观众列表V2 获取频道观众列表

  • 问题内容: 我正在玩Golang,我创建了这个小应用程序,使用goroutines进行了多个并发的api调用。 当应用程序运行时,调用完成后,该应用程序将卡住,这是有道理的,因为由于通道未关闭,无法退出 范围c 循环。 我不确定在哪种情况下可以更好地关闭该通道。 问题答案: 当没有更多值要发送时,您将关闭通道,因此在这种情况下,所有goroutine已完成。 (请注意,from 仅将反映连接和协议

  • 想知道是否有某种“钩子”来放置apache beam管道关闭时将执行的一段代码(无论出于何种原因-崩溃、取消) 每次数据流停止时,我都需要删除pubsub主题的订阅。

  • 关闭通道的意思是该通道将不再允许写入数据。这个方法可以让通道数据的接受端知道数据已经全部发送完成了。 package main import "fmt" // 在这个例子中,我们使用通道jobs在main函数所在的协程和一个数据 // 接收端所在的协程通信。当我们数据发送完成后,我们关闭jobs通道 func main() { jobs := make(chan int, 5) d

  • 关闭RSS频道 停止内容之播放后,于显示频道一览时按下按钮。 提示 关闭RSS频道之行为并无法同时切断网络联机。若想切断联机,请将无线LAN按钮调整为OFF(关)。