Введение
У автора раньше был небольшой опыт программирования NIO, поэтому фундамент этой области относительно слаб. Как важный модуль в java-программировании, NIO не может его освоить очень хорошо. Я чувствую, что у меня недостаточно знаний в java. Поэтому далее , я буду изучать программирование NIO, так что эта серия статей не будет подразумевать очень глубокий анализ исходного кода, это чисто учебный курс, а также его можно понимать как авторские заметки для записи процесса изучения NIO, и я тоже надеюсь что такого рода статьи могут быть похожи на такие же. Это полезно для тех, кто хочет освоить программирование NIO.
2. Что такое НИО?
Java NIO (новый ввод-вывод) — это API-интерфейс ввода-вывода (начиная с Java 1.4), который может заменить стандартный API ввода-вывода Java.Java NIO обеспечивает другой способ работы с вводом-выводом, чем стандартный ввод-вывод. NIO можно понимать как неблокирующий ввод-вывод.Традиционный ввод-вывод чтения и записи может только блокировать выполнение, а потоки не могут выполнять другие действия во время чтения и записи ввода-вывода.Например, когда вызывается socket.read(), если на сервере нет данных передача, поток будет блокироваться все время, а сокеты могут быть настроены в неблокирующем режиме в NIO.
3. Разница между IO и NIO
- IO ориентирован на поток байтов и поток символов, а NIO ориентирован на буфер.
- IO — блокирующий режим, NIO — неблокирующий режим.
- NIO добавила концепцию селекторов, которые могут контролировать несколько каналов с помощью селекторов.
В-четвертых, концепции, связанные с NIO
Канал
Каналы в Java NIO похожи на потоки, но немного отличаются:
Вы можете как читать данные из канала, так и записывать данные в канал. Но потоковые операции чтения и записи обычно однонаправлены. Каналы можно читать и записывать асинхронно.
Данные в канале всегда сначала считываются в буфер или всегда записываются из буфера.
Реализация канала
Это реализации наиболее важных каналов в Java NIO:
-
FileChannel: чтение и запись данных из файлов.
-
DatagramChannel: может читать и записывать данные в сети через UDP.
-
SocketChannel: может читать и записывать данные в сети через TCP.
-
ServerSocketChannel: может прослушивать входящие TCP-соединения, например веб-сервер. SocketChannel создается для каждого нового входящего соединения.
Буфер (буфер)
Буфер — это, по сути, блок памяти, в который данные могут быть записаны, а затем прочитаны из него. Этот фрагмент памяти обернут как объект буфера NIO и предоставляет набор методов для легкого доступа к этому фрагменту памяти.
Основные свойства Buffer (буфера)
Атрибуты | Функции |
---|---|
capacity | емкость |
position | Указатель текущей позиции буфера, максимальная может быть емкость – 1 |
limit | Максимальные ограничения на чтение и запись буфера |
Схематическая диаграмма свойств буфера:
На приведенном выше рисунке показана схема вышеперечисленных свойств в режиме записи и режиме чтения.В режиме записи предел и емкость одинаковы, что означает максимальную емкость данных, которые вы можете записать.В режиме чтения предел будет таким же, как положение, указывающее, что все записанные данные могут быть прочитаны.
Основная классификация Buffer (буфера)
- ByteBuffer
- MappedByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
На самом деле вышеописанное в основном соответствует получению разных типов данных, есть только один специальный MappedByteBuffer, его я не буду учить в этот раз, а узнаю позже.
5. Настоящий бой
В основном мы реализуем простое программирование NIO с целью понимания представленных выше концепций, чтения файлов в папке и последующего вывода на консоль.
public static void testNio(){
try {
RandomAccessFile rdf=new RandomAccessFile("E:\\nio\\niotest.txt","rw");
//利用channel中的FileChannel来实现文件的读取
FileChannel inChannel= rdf.getChannel();
//设置缓冲区容量为10
ByteBuffer buf= ByteBuffer.allocate(10);
//从通道中读取数据到缓冲区,返回读取的字节数量
int byteRead=inChannel.read(buf);
//数量为-1表示读取完毕。
while (byteRead!=-1){
//切换模式为读模式,其实就是把postion位置设置为0,可以从0开始读取
buf.flip();
//如果缓冲区还有数据
while (buf.hasRemaining()){
//输出一个字符
System.out.print((char) buf.get());
}
//数据读完后清空缓冲区
buf.clear();
//继续把通道内剩余数据写入缓冲区
byteRead = inChannel.read(buf);
}
//关闭通道
rdf.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Содержимое файла niotest.txt на моем диске — это hello world, давайте посмотрим, будет ли полученный результат hello world.
Результат такой, как и ожидалось. Мы завершили программирование NIO для вывода содержимого файла на диске. На самом деле, в приведенном выше коде мое первое впечатление сомнения заключается в том, чтоbuf.flip()
, Другие, кто использовал IO, должны быть в состоянии понять. Поскольку мы не можем этого понять, давайте сначала отладим его, закомментируем содержимое этого абзаца и посмотрим, каков результат.
закомментируйтеbuf.flip()
После этого результат вывода действительно неправильный, так почему?
buf.hasRemaining()
ложно, поэтому буфер ничего не выводит в первый раз. посмотри на вторую петлю
На самом деле весь цикл можно разобрать следующим образом:
-
Буфер считывает 10 байт содержимого, содержимое: hello worl.
-
Если buf.hasRemaining() считается ложным, очистите буфер напрямую (непосредственно сбросьте позицию в 0, что может напрямую перезаписать содержимое) и прочитайте оставшееся содержимое.
-
в конце остался только один символ
d
Прочтите, после прочтения на этот раз содержимое буфера выглядит следующим образом: dello worl. -
Окончательный вывод выводится из position=1, поэтому вывод: ello worl.
В соответствии с приведенным выше анализом мы обнаружили, что после аннотирования buf.flip(), какова позиция, в которой позиция записывается, и какова позиция чтения, поэтому метод должен заключаться в том, чтобы присвоить позиции 0 и читать с начала . Интерпретация исходного кода также подтвердила мою догадку.Исходный код выглядит следующим образом:
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
Ссылаться на
"JAVA NIO》
Рекомендуемое чтение
"ReentrantLock блокировки Java (1)》