Стандартный ввод-вывод Java против нового ввода-вывода

Java Новый ввод/вывод(NIO) был представлен в JDK 1.4. Продолжая работу над Standard IO, NIO обеспечивает высокоскоростной, блочно-ориентированный ввод-вывод в библиотеку Java.

Определяя классы для хранения данных и обрабатывая эти данные в блоках, NIO использует преимущества низкоуровневой оптимизации таким образом, который не мог бы использовать пакет java.io без использования собственного кода.

В этой статье мы сосредоточимся на выявлении наиболее заметных различий между стандартным и новым IO, которые мы должны знать, прежде чем решить, какой из них использовать в нашем следующем проекте.

Вызов стандартного ввода-вывода

Java IO относится к интерфейсу между компьютером и остальным миром или между отдельной программой и остальным компьютером.

В программировании на Java классы ввода-вывода до недавнего времени выполнялись с использованием метафоры потока. Все операции ввода-вывода рассматриваются как движение отдельных байтов, по одному за раз, через объект, называемый потоком.

Потоковый ввод-вывод используется для связи с внешним миром. Он также используется внутри, для превращения объектов в байты, а затем обратно в объекты. Это известно как сериализация и десериализация.

Представляем новый ввод-вывод Java

Java NIO был создан для того, чтобы позволить программистам Java реализовывать высокоскоростные операции ввода-вывода без необходимости написания собственного собственного кода.

NIO переносит наиболее трудоемкие операции ввода-вывода(а именно заполнение и опустошение буферов) обратно в операционную систему, что позволяет значительно увеличить скорость.

Если вышеизложенные вступления оставили вас жаждущими, то не волнуйтесь, если вы почувствуете себя лучше, когда мы двинемся дальше. Давайте начнем с поиска различий.

Различия между IO и NIO

Потоки ввода-вывода против блоков NIO

Самое важное различие между стандартной библиотекой ввода-вывода(java.io.*) и новой библиотекой ввода-вывода(java.nio.*) заключается в том, как данные упаковываются и передаются от источника к цели. Как упоминалось ранее, стандартный ввод-вывод работает с данными в потоках, тогда как NIO работает с данными в блоках.

Потоковая система ввода-вывода обрабатывает данные по одному или более байтам за раз. Входной поток производит один байт данных, а выходной поток потребляет один байт данных. Очень легко создавать фильтры для потоковых данных. Также относительно просто объединить несколько фильтров вместе, чтобы каждый из них выполнял свою часть в том, что составляет единый сложный механизм обработки.

Важно то, что байты нигде не кэшируются. Более того, мы не можем перемещаться вперед и назад по данным в потоке. Если вам нужно перемещаться вперед и назад по данным, считанным из потока, мы должны сначала кэшировать их в буфере.

Блочно-ориентированная система ввода-вывода работает с данными в блоках. Каждая операция производит или потребляет блок данных за один шаг. Обработка данных блоком может быть намного быстрее, чем обработка их(потоковым) байтом. Вы можете перемещаться вперед и назад в буфере по мере необходимости.

Блоки данных дают нам немного больше гибкости во время обработки. Однако нам также нужно проверить, содержит ли буфер все необходимые нам данные для полной обработки. И нам нужно убедиться, что при считывании дополнительных данных в буфер мы не перезапишем данные в буфере, который мы еще не обработали.

С другой стороны, блочно-ориентированный ввод-вывод лишен некоторой элегантности и простоты потокового ввода-вывода.

Подробнее: 3 способа чтения файлов с помощью Java NIO

Синхронный стандарт против асинхронного нового ввода-вывода

Различные потоки Java IO являются блокирующими или синхронными. Это означает, что когда поток вызывает операцию read() или write(), этот поток блокируется до тех пор, пока не появятся данные для чтения или данные не будут полностью записаны. В течение этого периода поток будет находиться в заблокированном состоянии. Это было названо веским основанием для внедрения многопоточности в современные языки.

В асинхронном вводе-выводе поток может запросить запись некоторых данных в канал, но не ждать, пока они будут полностью записаны. Затем поток может продолжить и сделать что-то еще в это время. Обычно эти потоки тратят свое время простоя, когда они не заблокированы в вызовах ввода-вывода, обычно выполняя ввод-вывод на других каналах в это время. То есть один поток теперь может управлять несколькими каналами ввода-вывода.

Синхронным программам часто приходится прибегать к опросу или созданию множества потоков для работы с большим количеством соединений. С помощью асинхронного ввода-вывода вы можете прослушивать события ввода-вывода на произвольном количестве каналов, без опроса и дополнительных потоков.

Центральный объект в асинхронном вводе-выводе называется Селектор. Селектор — это то место, где вы регистрируете свой интерес к различным событиям ввода-вывода, и это объект, который сообщает вам, когда эти события происходят. Итак, первое, что нам нужно сделать, — это создать Селектор.

Selector selector = Selector.open();

Позже мы вызовем метод register() для различных объектов Channel, чтобы зарегистрировать наш интерес к событиям IO, происходящим внутри этих объектов. Первым аргументом register() всегда является Selector.

Подробнее: Как определить путь в Java NIO

Java IO против API NIO

Не будет награды за догадку, что вызовы API при использовании NIO выглядят иначе, чем при использовании IO. Здесь, в NIO, вместо того, чтобы просто считывать данные байт за байтом, например, из InputStream, данные сначала должны быть считаны в буфер, а затем обработаны оттуда.

Пример Java для чтения файла с использованием стандартного ввода-вывода.

import java.io.BufferedReader;import java.io.FileReader;import java.io.IOException;public class WithoutNIOExample{public static void main(String[] args){BufferedReader br = null;String sCurrentLine = null;try{br = new BufferedReader(new FileReader("test.txt"));while((sCurrentLine = br.readLine()) != null){System.out.println(sCurrentLine);}}catch(IOException e){e.printStackTrace();}finally{try{if(br != null)br.close();} catch(IOException ex){ex.printStackTrace();}}}}

Пример Java для чтения файла с использованием New IO.

import java.io.IOException;import java.io.RandomAccessFile;import java.nio.ByteBuffer;import java.nio.channels.FileChannel;public class ReadFileWithFixedSizeBuffer{public static void main(String[] args) throws IOException{RandomAccessFile aFile = new RandomAccessFile("test.txt", "r");FileChannel inChannel = aFile.getChannel();ByteBuffer buffer = ByteBuffer.allocate(1024);while(inChannel.read(buffer) > 0){buffer.flip();for(int i = 0; i < buffer.limit(); i++){System.out.print((char) buffer.get());}buffer.clear(); // do something with the data and clear/compact it.}inChannel.close();aFile.close();}}

Заключение

NIO позволяет управлять несколькими каналами, используя только один(или меньше) потоков, но за это приходится платить тем, что анализ данных может оказаться несколько сложнее, чем при чтении данных из блокирующего потока с использованием стандартного ввода-вывода.

Если вам нужно управлять тысячами открытых соединений одновременно, каждое из которых отправляет лишь немного данных, например, сервер чата, реализация сервера в NIO, вероятно, будет преимуществом. Аналогично, если вам нужно поддерживать много открытых соединений с другими компьютерами, например, в сети P2P, использование одного потока для управления всеми исходящими соединениями может быть преимуществом.

Если у вас мало подключений с очень высокой пропускной способностью и вы отправляете большой объем данных за раз, то вам следует выбрать стандартную реализацию сервера ввода-вывода.

Прокрутить вверх