什么是NIO
Java NIO (New IO)是 Java 的另一個 IO API (來自 java1.4) ,意味著可以替代標(biāo)準(zhǔn)的 Java IO API和 Java Networking API。提供了一種與標(biāo)準(zhǔn) IO API 不同的 IO 工作方式。注意:Java的NIO只指IO API,阻塞和不阻塞是IO的模型。
還有人稱NIO為無阻塞IO,非阻塞IO,但這么說并不嚴(yán)格。因為基本的IO操作API(比如文件IO、文件通道)還是一個阻塞模型。只有網(wǎng)絡(luò)輸入輸出應(yīng)用編程接口可以使用非阻塞模型(配置阻塞(假))。
Java NIO中的聯(lián)網(wǎng)IO API支持無阻塞IO模型,實現(xiàn)IO復(fù)用。對于服務(wù)器來說,它可以用更少的線程支持更多的并發(fā),大大提高了性能。
NIO中的阻塞和非阻塞
阻塞和非阻塞都是從線程的角度出發(fā),這里指的是線程狀態(tài)。
阻塞
做IO讀寫時,線程被阻塞。此時,cpu控制權(quán)將被放棄,cpu資源將不會被占用。
什么?不占用CPU資源?是不是說分塊模式更好?
答案是否定的,阻塞狀態(tài)雖然不會占用CPU,但是會造成線程切換,而且線程切換的時候會有一個上下文保存和轉(zhuǎn)換的過程,需要CPU調(diào)度,是一個非常昂貴的操作。
Java NIO中的基本IO API(非聯(lián)網(wǎng)IO API)還是一種阻塞方式,但是從面向流編程到緩沖區(qū)都在使用,本質(zhì)上和BIO沒什么區(qū)別。
無阻塞
非阻塞是指在執(zhí)行IO操作時,如果設(shè)備沒有準(zhǔn)備好(比如套接字沒有收到數(shù)據(jù)),操作會直接返回結(jié)果,不會導(dǎo)致當(dāng)前線程進入阻塞狀態(tài)。
這樣做的好處是,當(dāng)數(shù)據(jù)沒有準(zhǔn)備好時,用戶可以決定做什么。線程可以在沒有數(shù)據(jù)的情況下執(zhí)行其他操作。
網(wǎng)絡(luò)應(yīng)用編程接口可以配置為非阻塞模型通道。configureblocking (false),與選擇器結(jié)合實現(xiàn)復(fù)用功能。簡單來說,一個選擇器監(jiān)聽多個套接字io(對于unix系統(tǒng),套接字也是一個fd和io),可以在一個線程中支持多個連接。當(dāng)然,在實際的服務(wù)器開發(fā)中,即使是NIO模型,有些程序也不會只使用一個線程;但是相對于傳統(tǒng)的Blocking IO方式,所需線程數(shù)會大大減少。(redis使用IO復(fù)用技術(shù),只有一個線程監(jiān)聽socket io)
免疫球蛋白源的淀粉樣蛋白
AIO是在Java 1.7之后引入的包,Java 1.7是NIO的升級版本。它增加了異步和非阻塞IO操作模式,所以人們稱之為AIO(異步IO)。異步IO是基于事件和回調(diào)機制實現(xiàn)的,即應(yīng)用操作后直接返回,不在那里阻塞。后臺處理完成后,操作系統(tǒng)會執(zhí)行回調(diào),通知相應(yīng)的線程進行后續(xù)操作。
多路技術(shù)
在I/O編程過程中,當(dāng)需要同時處理多個客戶端請求時,可以采用多線程或I/O復(fù)用技術(shù)進行處理。I/O復(fù)用技術(shù)將多個I/O塊復(fù)用到同一個Select塊,使得系統(tǒng)可以在單線程的情況下同時處理多個客戶端請求。與傳統(tǒng)的多線程/多進程模型相比,I/O復(fù)用最大的優(yōu)點是系統(tǒng)不需要創(chuàng)建新的額外的進程或線程,也不需要維護這些線程和進程的運行,從而減少了系統(tǒng)的維護工作量,節(jié)約了系統(tǒng)資源。輸入輸出復(fù)用的主要應(yīng)用場景如下:
服務(wù)器需要同時處理多個處于監(jiān)聽狀態(tài)或者多個連接狀態(tài)的Socket服務(wù)器需要同時處理多種網(wǎng)絡(luò)協(xié)議的Socket目前支持I/O復(fù)用的系統(tǒng)調(diào)用是select/pselect/poll/epoll。
選擇/epoll
選擇
選擇的實現(xiàn)很簡單。如果程序同時監(jiān)控如下圖所示的sock1、sock2、sock3三個套接字,在調(diào)用select后,操作系統(tǒng)會將進程a分別添加到這三個套接字的等待隊列中。
當(dāng)任何一個套接字接收到數(shù)據(jù)時,中斷程序都會調(diào)用這個過程。下圖是sock2接收數(shù)據(jù)的處理流程。
喚醒一個進程意味著從所有等待隊列中移除該進程,并將其添加到工作隊列中。如下圖所示。
通過這些步驟,當(dāng)進程A醒來時,它知道至少有一個套接字接收到了數(shù)據(jù)。程序只需要遍歷一次套接字列表就可以得到準(zhǔn)備好的套接字。
這種簡單的方式是有效的,在幾乎所有的操作系統(tǒng)中都有相應(yīng)的實現(xiàn)。
然而,簡單的方法往往有缺點,主要是:
第一,每次調(diào)用select,都需要將進程添加到所有監(jiān)控套接字的等待隊列中,每次醒來都需要將其從每個隊列中移除。涉及到兩次遍歷,每次都要把整個fds列表傳遞給內(nèi)核,有一定的開銷。正是因為遍歷操作開銷大,為了效率才規(guī)定了select的最大監(jiān)控次數(shù),默認(rèn)只能監(jiān)控1024個socket。
第二,進程被喚醒后,程序不知道哪個套接字接收數(shù)據(jù),需要重新遍歷。
那么,有什么方法可以減少遍歷呢?有沒有辦法保存一個現(xiàn)成的套接字?這兩個問題都要通過epoll技術(shù)來解決。
補充說明:本節(jié)只解釋了select的一種情形。當(dāng)程序調(diào)用select時,內(nèi)核會先遍歷一遍socket,如果有一個以上的socket接收緩沖區(qū)有數(shù)據(jù),那么select直接返回,不會阻塞。這也是為什么select的返回值有可能大于1的原因之一。如果沒有socket有數(shù)據(jù),進程才會阻塞。select效率低的原因之一是結(jié)合了“維護等待隊列”和“阻塞進程”兩個步驟。如下圖所示,每次調(diào)用選擇都需要這兩個操作。然而,在大多數(shù)應(yīng)用場景中,要監(jiān)控的套接字是相對固定的,不需要每次都進行修改。Epoll將這兩個操作分開,首先使用epoll_ctl維護等待隊列,然后調(diào)用epoll_wait阻塞進程。顯然可以提高效率。
select效率低的另一個原因是程序不知道哪個套接字接收數(shù)據(jù),只能逐個遍歷。如果內(nèi)核維護一個“就緒列表”,并引用接收數(shù)據(jù)的套接字,就可以避免遍歷。如下圖所示,計算機中有三個socket,接收數(shù)據(jù)的sock2和sock3被rdlist引用。當(dāng)進程被喚醒時,您可以通過獲取rdlist的內(nèi)容來知道哪個套接字接收數(shù)據(jù)。
使用
Epall是select出現(xiàn)n多年后發(fā)明的,是select和poll的增強版。Epoll通過以下措施提高效率。
原則:
創(chuàng)建epoll對象
如下圖所示,當(dāng)一個進程調(diào)用epoll_create方法時,內(nèi)核會創(chuàng)建一個eventpoll對象(即程序中epfd表示的對象)。Eventpoll對象也是文件系統(tǒng)的成員,和socket一樣,它也有一個等待隊列。
有必要創(chuàng)建一個表示epoll的eventpoll對象,因為內(nèi)核需要維護諸如Ready List之類的數(shù)據(jù),這些數(shù)據(jù)可以用作eventpoll的成員。
維護觀察列表
創(chuàng)建epoll對象后,可以使用epoll_ctl來添加或刪除您想要監(jiān)聽的套接字。以添加套接字為例,如下圖所示,如果通過epoll_ctl添加sock1、sock2、sock3的監(jiān)控,那么內(nèi)核會在這三個套接字的等待隊列中添加eventpoll。
當(dāng)套接字接收到數(shù)據(jù)時,中斷程序?qū)⒉僮鱡ventpoll對象,而不是直接操作進程。
接收數(shù)據(jù)
當(dāng)套接字接收到數(shù)據(jù)時,中斷程序會將套接字引用添加到eventpoll的“就緒列表”中。下圖顯示sock2和sock3收到數(shù)據(jù)后,中斷程序讓rdlist引用這兩個套接字。
Eventpoll對象相當(dāng)于套接字和進程之間的中介。socket的數(shù)據(jù)接收并不直接影響進程,而是通過改變eventpoll的就緒列表來改變進程的狀態(tài)。
當(dāng)程序執(zhí)行到epoll_wait時,如果rdlist已經(jīng)引用socket,則epoll_wait直接返回,如果rdlist為空,則阻塞進程。
阻斷和喚醒過程
假設(shè)進程A和進程B在計算機中運行,進程A在某個時刻運行epoll_wait語句。如下圖所示,內(nèi)核會將進程A放入eventpoll的等待隊列,阻塞進程。
當(dāng)套接字接收到數(shù)據(jù)時,中斷程序一方面修改rdlist,另一方面喚醒eventpoll等待隊列中的進程,進程A再次進入運行狀態(tài)(如下圖)。由于rdlist,進程a可以知道哪個套接字發(fā)生了變化。
指
Netty權(quán)威指南https://zhuanlan.zhihu.com/p/64138532http://tutorials.jenkov.com/java-nio/index.html空無
https://segmentfault.com/a/1190000019814737
1.《nio 一篇文章帶你徹底搞懂NIO》援引自互聯(lián)網(wǎng),旨在傳遞更多網(wǎng)絡(luò)信息知識,僅代表作者本人觀點,與本網(wǎng)站無關(guān),侵刪請聯(lián)系頁腳下方聯(lián)系方式。
2.《nio 一篇文章帶你徹底搞懂NIO》僅供讀者參考,本網(wǎng)站未對該內(nèi)容進行證實,對其原創(chuàng)性、真實性、完整性、及時性不作任何保證。
3.文章轉(zhuǎn)載時請保留本站內(nèi)容來源地址,http://f99ss.com/tiyu/1005535.html