Ио не чувствует себя очень связанным с многопоточным, но NIO изменил способ использования потоков на уровне приложения и решил некоторые практические трудности. AIO является асинхронным IO, и предыдущая серия также связана. Здесь, для обучения и записи, я также написал статью, чтобы представить NIO и AIO.
1. Что такое Nio
NIO является аббревиатурой нового ввода-вывода, который противоположна старому методу ввода-вывода на основе старого потока. Судя по названию, он представляет собой новый набор стандартов ввода/вывода Java. Он был включен в JDK в Java 1.4 и имеет следующие функции:
Все операции чтения и записи из канала должны проходить через буфер, а канал является абстракцией IO, а другой конец канала - манипулированный файл.
2. Буфер
Реализация буфера в Java. Основные типы данных имеют соответствующие буферы
Простые примеры использования буфера:
Пакет -тест; Импорт java.io.file; импорт java.io.fileinputstream; импорт java.nio.bytebuffer; импорт java.nio.channels.filechannel; открытый тест класса {public static void main (string [] args) выбрасывает Exception {fileInputStream fin = new FileInputStream (new File ("d: //temp_buffer.tmp")); FileChannel fc = fin.getChannel (); Bytebuffer bytebuffer = bytebuffer.allocate (1024); fc.read (bytebuffer); fc.close (); bytebuffer.flip (); // Читать и записать преобразование}}Шаги для использования суммируются:
1. Получить канал
2. Подайте заявку на буфер
3. Установить взаимосвязь чтения/записи между каналом и буфером
4. Закрыть
Следующим примером является использование NIO для копирования файлов:
public static void niocopyfile (строковый ресурс, строковое назначение) бросает ioexception {fileInputStream fis = new FileInputStream (resource); FileOutputStream fos = new FileOutputStream (destination); FileChannel ReadChannel = fis.getChannel (); // Читать канал файла fileChannel WriteChannel = fos.getChannel (); // Написать канал файла bytebuffer buffer = bytebuffer.allocate (1024); // Читать в кэше данных while (true) {buffer.clear (); int len = readchannel.read (buffer); // Читать в данных if (len == -1) {break; // Читать в} buffer.flip (); writeChannel.write (буфер); // записать в файл} readchannel.close (); writeChannel.close (); }В буфере есть 3 важных параметра: положение, емкость и предел
Здесь нам нужно различать емкость и верхний предел. Например, если буфер имеет 10 КБ, то 10 КБ - это емкость. Если я прочитаю файл 5 КБ в буфере, то верхний предел составляет 5 КБ.
Вот пример, чтобы понять эти 3 важных параметров:
public static void main (string [] args) бросает исключение {bytebuffer b = bytebuffer.allocate (15); // 15-байтовая буферная система.out.println ("limit =" + b.limit () + "емкость =" + b.capacity () + "position =" + b.position ()); for (int i = 0; i <10; i ++) {// Сохранить 10 байт данных b.put ((byte) i); } System.out.println ("limit =" + b.limit () + "емкость =" + b.capacity () + "position =" + b.position ()); b.flip (); // сбросить систему положения. for (int i = 0; i <5; i ++) {System.out.print (b.get ()); } System.out.println (); System.out.println ("Limit =" + b.limit () + "емкость =" + b.capacity () + "position =" + b.position ()); b.flip (); System.out.println ("Limit =" + b.limit () + "емкость =" + b.capacity () + "position =" + b.position ()); }Весь процесс показан на рисунке:
В настоящее время позиция составляет от 0 до 10, а емкость и предел остаются неизменными.
Эта операция сбрасывает позицию. Обычно при преобразовании буфера из режима записи в режим чтения необходимо выполнить этот метод. Операция Flip () не только сбрасывает текущую позицию до 0, но также устанавливает предел в положение текущего положения.
Значение предела - определить, какие данные имеют смысл. Другими словами, данные от позиции до ограничения являются значимыми данными, потому что это данные из последней операции. Следовательно, флип -операции часто означают конверсию чтения и письма.
То же значение, что и выше.
Большинство методов в буфере изменяют эти 3 параметра для достижения определенных функций:
публичный финальный буфер Rewind ()
Установить позицию ноль и четкую марку
публичный финальный буфер clear ()
Установите положение положения и установите ограничение на размер емкости и очистите марку флага
публичный финальный буфер flip ()
Сначала установите ограничение в позицию, в которой находится положение, затем установите положение в нулевой
Файл сопоставлен с памятью
public static void main (string [] args) бросает исключение {randomAccessfile raf = new randomAccessfile ("c: //mapfile.txt", "rw"); FileChannel fc = raf.getChannel (); // отобразить файл в память сопоставленным образом, mbb = fc.map (filechannel.mapmode.read_write, 0, raf.length ()); while (mbb.hasreming ()) {System.out.print ((char) mbb.get ()); } mbb.put (0, (байт) 98); // изменять файл raf.close (); }Модификация MapedByteBuffer эквивалентна изменению самого файла, поэтому скорость работы очень быстрая.
3. канал
Общая структура многопоточного сетевого сервера:
Простой многопоточный сервер:
public static void main (string [] args) бросает исключение {serversocket echoserver = null; Socket Clientsocket = null; try {echoserver = new Serversocket (8000); } catch (ioException e) {System.out.println (e); } while (true) {try {clientocket = echoserver.accept (); System.out.println (clientockethocket.getRemoteSocketAddress () + "Connect!"); tp.execute (new Handlemsg (Clientsocket)); } catch (ioException e) {System.out.println (e); }}}Функция состоит в том, чтобы записать данные клиенту, когда сервер читает.
Здесь TP - это пул потоков, а HandLemsg - это класс, который обрабатывает сообщения.
Статический класс handlemsg реализует runnable {unmit part of Information public void run () {try {is = new BufferedReader (новый inputStreamReader (clientSocketocket.getInputStream ())); OS = New PrintWriter (clientsocket.getOutputStream (), true); // Читать данные, отправленные клиентом из inputStream строки inputline = null; long b = system.currenttimemillis (); while ((inputline = is.readline ())! = null) {os.println (inputline); } long e = Система. CurrentTimemiLlis (); Система. out.println ("потратить:"+(e - b)+"ms"); } catch (ioException e) {e.printstackTrace (); } наконец {закрыть ресурс}}}Клиент:
public static void main (string [] args) бросает исключение {socket client = null; PrintWriter Writer = null; BufferedReader Reader = null; try {client = new Socket (); client.connect (new inetsocketAddress ("localhost", 8000)); writer = new PrintWriter (client.getOutputStream (), true); writer.println ("Привет!"); writer.flush (); reader = new BufferedReader (новый inputStreamReader (client.getInputStream ())); System.out.println ("From Server:" + reader.readline ()); } catch (Exception e) {} наконец {// Опустить закрытие ресурса}}Приведенное выше сетевое программирование очень простое, и будут возникнуть некоторые проблемы с использованием этого метода:
Используйте один поток для каждого клиента. Если клиент испытывает исключение, такое как задержка, поток может быть занят в течение длительного времени. Потому что подготовка данных и чтение находятся в этой теме. В настоящее время, если есть много клиентов, это может потреблять много системных ресурсов.
Решение:
Используйте неблокирующий NIO (прочитайте данные без ожидания, данные готовы перед работе)
Чтобы отразить эффективность использования NIO.
Здесь мы сначала имитируем неэффективный клиент для моделирования задержки из -за сети:
частное статическое исполнители TP = Executors.NewCachedThreadPool (); Private Static Final int sleep_time = 1000*1000*1000; public static class echoclient реализует runnable {public void run () {try {client = new Socket (); client.connect (new inetsocketAddress ("localhost", 8000)); writer = new PrintWriter (client.getOutputStream (), true); writer.print ("h"); Locksupport.parknanos (sleep_time); writer.print ("e"); Locksupport.parknanos (sleep_time); writer.print ("l"); Locksupport.parknanos (sleep_time); writer.print ("l"); Locksupport.parknanos (sleep_time); writer.print ("o"); Locksupport.parknanos (sleep_time); writer.print ("!"); Locksupport.parknanos (sleep_time); writer.println (); writer.flush (); } catch (Exception e) {}}}Вывод на стороне сервера:
Потратить: 6000 мс
Потратить: 6000 мс
Потратить: 6000 мс
Потратить: 6001 мс
Потратить: 6002 мс
Потратить: 6002 мс
Потратить: 6002 мс
Потратить: 6002 мс
Потратить: 6003 мс
Потратить: 6003 мс
потому что
while ((inputline = is.readline ())! = null)
Он заблокирован, поэтому время тратится на ожидание.
Что произойдет, если NIO использовался для решения этой проблемы?
Одна из больших особенностей NIO: уведомлять меня, если данные готовы
Канал немного похож на потоки, и канал может соответствовать файлам или сетевым розеткам.
Селектор - это селектор, который может выбрать канал и что -то сделать.
Поток может соответствовать селектору, а селектор может опросить несколько каналов, а у каждого канала есть розетка.
По сравнению с вышеупомянутым потоком, соответствующим гнездам, после использования NIO потока может опросить несколько розеток.
Когда селектор вызывает Select (), он проверит, подготовил ли какой -либо клиент данные. Когда данные не будут готовы, Select () будет блокировать. Обычно говорят, что NIO не блокирует, но если данные не готовы, все равно будет блокировать.
Когда данные будут готовы, после вызова select (), SelectionKey будет возвращен. SelectionKey указывает, что данные канала на селекторе были подготовлены.
Этот канал будет выбран только тогда, когда данные будут готовы.
Таким образом, NIO реализует поток для мониторинга нескольких клиентов.
Клиент с только что смоделированной сетевой задержкой не повлияет на потоки в NIO, потому что, когда сеть сокетов задержается, данные не готовы, а селектор не выберет его, но выберет других подготовленных клиентов.
Разница между selectnow () и select () заключается в том, что SelectNow () не блокирует. Когда ни один клиент готовит данные, SelectNow () не будет блокировать и вернет 0. Когда клиент готовит данные, SelectNow () возвращает количество подготовленных клиентов.
Основной код:
Пакет -тест; Импорт java.net.inetaddress; import java.net.inetsocketAddress; импорт java.net.socket; import java.nio.channels.selectionkey; импорт java.nio.channels.selector; import java.nio.channels.selector; import java.nio.shannels.sergersockanne; java.nio.channels.socketchannel; import java.nio.channels.spi.abstractselector; import java.nio.channels.spi.selectorprovider; import java.util.hashmap; impormal.ut. java.util.concurrent.executorservice; import java.util.concurrent.executors; открытый класс MultiThreadnioeChoserver {public Static Map <сокет, long> geym_time_stat = new Hashmap <сокет, long> (); класс Echoclient {private LinkedList <bytebuffer> outq; Echoclient () {outq = new LinkedList <bytebuffer> (); } public LinkedList <ByteBuffer> getOutputQueue () {return outq; } public void enqueue (bytebuffer bb) {outq.addfirst (bb); }} класс HandLemsg реализует runnable {selectionKey sk; Bytebuffer BB; public handlemsg (selectionkey sk, bytebuffer bb) {super (); this.sk = sk; this.bb = bb; } @Override public void run () {// todo Автогенерированный метод atub echoclient echoclient = (echoclient) sk.attachment (); echoclient.enqueue (bb); Sk.Interestops (selectionKey.op_read | selectionKey.op_write); selector.wakeup (); }} Частный селекторный селектор; private executorservice tp = executors.newcachedthreadpool (); private void Starterver () Throws Exception {selector = selectorProvider.provider (). OpenSelector (); ServerSocketchannel SSC = serversocketchannel.open (); ssc.configureblocking (false); InetsocketAddress isa = new InetSocketAddress (8000); ssc.socket (). Bind (ISA); // зарегистрировать интересующее событие, здесь заинтересовано в выборе событий Accpet CackKey = ssc.register (селектор, selectionKey.op_accte); for (;;) {selector.select (); SETEADEKEYS = SELECTER.SELECTEDKEYS (); Итератор I = readiouskeys.iterator (); длинный e = 0; while (i.hasnext ()) {selectionKey sk = (selectionKey) i.next (); I.Remove (); if (sk.isaceceptable ()) {doaccept (sk); } else if (sk.isvalid () && sk.isReadable ()) {if (! geym_time_stat.containskey ((((socketchannel) sk .channel ()). Socket ())) {geym_time_stat.put (((socketchannel) sk.channel ().). } doread (SK); } else if (sk.isvalid () && sk.iswrite ()) {dowrite (sk); e = System.currentTimeMillis (); long b = geym_time_stat.remove ((((socketchannel) sk .channel ()). Socket ()); System.out.println ("ront:" + (e - b) + "ms"); }} / Echoclient echoclient = (echoclient) sk.attachment (); LinkedList <bytebuffer> outq = echoclient.getOutputQueue (); Bytebuffer bb = outq.getlast (); try {int len = channel.write (bb); if (len == -1) {deancnect (sk); возвращаться; } if (bb.reming () == 0) {outq.removelast (); }} catch (Exception e) {// todo: обрабатывать исключение Dinsnect (sk); } if (outq.size () == 0) {sk.Interestops (selectionKey.op_read); }} private void doread (selectionKey sk) {// todo автоматически сгенерированный метод Socketchannel Channel = (socketchannel) sk.channel (); Bytebuffer bb = bytebuffer.allocate (8192); int len; try {len = channel.read (bb); if (len <0) {deancnect (sk); возвращаться; }} catch (Exception e) {// todo: обрабатывать исключение Dinsnect (sk); возвращаться; } bb.flip (); tp.execute (new Handlemsg (SK, BB)); } private void Deancnect (SelectionKey SK) {// TODO Auto Generated Method Stub // Опустить операцию сухого закрытия} private void doaccept (selectionKey SK) {// toDo Автогенерированный метод STUB ServerSocketchannel Server = (ServerSocketchannel) sk.Channel (); Socketchannel ClientChannel; try {clientChannel = server.accep (); clientChannel.configureblocking (false); SelectionKey clientKey = clientChannel.register (selector, selectionKey.op_read); Echoclient echoclinet = new Echoclient (); ClientKey.attach (echoclinet); InetAddress clientAddress = clientChannel.socket (). GetInetAddress (); System.out.println ("Принятое соединение из" + clientAddress.gethostaddress ()); } catch (Exception e) {// todo: обрабатывать исключение}} public static void main (string [] args) {// todo с генерируемым методом static multiThreadnioeChoserver echoserver = new MultiThreadnioeChoserver (); try {echoserver.startServer (); } catch (Exception e) {// todo: exception}}}}Код предназначен только для справки, и его основная особенность заключается в том, что вы заинтересованы в разных событиях, чтобы делать разные вещи.
При использовании отсроченного клиента, который был смоделирован ранее, потребление времени на этот раз составляет от 2 мс до 11 мс. Повышение производительности очевидно.
Суммировать:
1. После того, как NIO подготовит данные, он передаст их приложению для обработки. Процесс считывания/записи данных по -прежнему завершен в потоке приложения, и он лишь лишь время ожидания в отдельном потоке.
2. Сохранить время подготовки данных (потому что селектор может быть повторно использован)
5. aio
Особенности AIO:
1. Уведомите меня после прочтения
2. IO не будет ускорен, но будет уведомлен после прочтения
3. Используйте функции обратного вызова для выполнения бизнес -обработки
Код, связанный с AIO:
Asynchronousserversocketchannel
server = asynchronousserversocketchannel.open (). bind (new inetsocketaddress (порт));
Используйте метод принятия на сервере
Общественный абстрактный <a> void принять (привязанность, завершающий мандлер <asynchronoussocketchenlancel,? Супер a> handler);
OpplionHandler - это интерфейс обратного вызова. Когда есть клиент, принимающий, он делает то, что находится в обработке.
Пример кода:
Server.accept (null, новый завершение handhandler <asynchronoussocketchannel, object> () {final bytebuffer buffer = bytebuffer.allocate (1024); public void завершен (Asynchronoussocketchannel Result, Object Attachment) {system.out.println (thread.currentThread (). Buffer.clear (); это);Здесь мы используем будущее для мгновенного возврата. Для будущего, пожалуйста, обратитесь к предыдущей статье
Основываясь на понимании NIO, глядя на AIO, разница в том, что AIO ждет, пока процесс чтения и записи вызовет функцию обратного вызова перед завершением.
Nio синхронно и не блокирует
AIO асинхронно и не блокирует
Поскольку процесс чтения и записи NIO по -прежнему завершен в потоке приложения, NIO не подходит для тех, кто имеет длительный процесс чтения и записи.
Процесс чтения и записи AIO уведомляется только после его завершения, поэтому AIO компетентен для тяжелых и долгосрочных задач процесса чтения и записи.