Автор оригинала: Mona Mohamadinia.
1. Обзор
С введением лямбда-выражений в Java 8 стало возможным писать код более лаконичным и функциональным способом. Потоки и функциональные интерфейсы являются сердцем этого революционного изменения в платформе Java.
В этом кратком руководстве мы узнаем, следует ли явно закрывать потоки Java 8, рассматривая их с точки зрения ресурсов.
2. Закрытие потоков
Java 8 потоки реализуют автоклавируемый интерфейс:
public interface Streamextends BaseStream<...> { // omitted } public interface BaseStream<...> extends AutoCloseable { // omitted }
Проще говоря, мы должны думать о потоках как о ресурсах, которые мы можем заимствовать и возвращать, когда закончим с ними. В отличие от большинства ресурсов, нам не нужно всегда закрывать потоки.
Поначалу это может показаться нелогичным, поэтому давайте посмотрим, когда мы должны и когда не должны закрывать потоки Java 8.
2.1. Коллекции, массивы и генераторы
Большую часть времени мы создаем экземпляры Stream из коллекций Java, массивов или функций генератора. Например, здесь мы работаем с коллекцией String через Stream API:
Listcolors = List.of("Red", "Blue", "Green") .stream() .filter(c -> c.length() > 4) .map(String::toUpperCase) .collect(Collectors.toList());
Иногда мы генерируем конечный или бесконечный последовательный поток:
Random random = new Random(); random.ints().takeWhile(i -> i < 1000).forEach(System.out::println);
Кроме того, мы также можем использовать потоки на основе массивов:
String[] colors = {"Red", "Blue", "Green"}; Arrays.stream(colors).map(String::toUpperCase).toArray()
Когда мы имеем дело с такого рода потоками, мы не должны закрывать их явно. Единственным ценным ресурсом, связанным с этими потоками, является память, и Сборка мусора (GC) заботится об этом автоматически.
2.2. Ресурсы ввода-вывода
Однако некоторые потоки поддерживаются ресурсами ввода-вывода, такими как файлы или сокеты. Например, метод Files.lines() передает потоки всех строк для данного файла:
Files.lines(Paths.get("/path/to/file")) .flatMap(line -> Arrays.stream(line.split(","))) // omitted
Под капотом этот метод открывает экземпляр FileChannel , а затем закрывает его при закрытии потока. Таким образом, если мы забудем закрыть поток, базовый канал останется открытым, и тогда мы получим утечку ресурсов .
Чтобы предотвратить такие утечки ресурсов, настоятельно рекомендуется использовать идиому try-with-resources для закрытия потоков на основе ввода-вывода:
try (Streamlines = Files.lines(Paths.get("/path/to/file"))) { lines.flatMap(line -> Arrays.stream(line.split(","))) // omitted }
Таким образом, компилятор автоматически закроет канал. Ключевым моментом здесь является закрытие всех потоков на основе ввода-вывода .
Пожалуйста, обратите внимание, что закрытие уже закрытого потока приведет к Исключение IllegalStateException .
3. Заключение
В этом коротком уроке мы увидели различия между простыми потоками и тяжелыми потоками ввода-вывода. Мы также узнали, как эти различия влияют на наше решение о том, закрывать или нет потоки Java 8.
Как обычно, пример кода доступен на GitHub .