日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

NIO相比BIO的優(yōu)勢

NIO(Non-blocking I/O,在JAVA領(lǐng)域,也稱為New I/O),是一種同步非阻塞的I/O模型,也是I/O多路復(fù)用的基礎(chǔ),已經(jīng)被越來越多地應(yīng)用到大型應(yīng)用服務(wù)器,成為解決高并發(fā)與大量連接、I/O處理問題的有效方式。

資深架構(gòu)師總結(jié):徹底搞懂NIO效率高的原理

 

面向流與面向緩沖

Java NIO和BIO之間第一個(gè)最大的區(qū)別是,BIO是面向流的,NIO是面向緩沖區(qū)的。 JavaIO面向流意味著每次從流中讀一個(gè)或多個(gè)字節(jié),直至讀取所有字節(jié),它們沒有被緩存在任何地方。 此外,它不能前后移動(dòng)流中的數(shù)據(jù)。 如果需要前后移動(dòng)從流中讀取的數(shù)據(jù),需要先將它緩存到一個(gè)緩沖區(qū)。 Java NIO的緩沖讀取方法略有不同。 數(shù)據(jù)讀取到一個(gè)緩沖區(qū),需要時(shí)可在緩沖區(qū)中前后移動(dòng)。 這就增加了處理過程中的靈活性。 但是,還需要檢查是否該緩沖區(qū)中包含所有需要處理的數(shù)據(jù)。 而且,需確保當(dāng)更多的數(shù)據(jù)讀入緩沖區(qū)時(shí),不要覆蓋緩沖區(qū)里尚未處理的數(shù)據(jù)。

阻塞IO與非阻塞IO

Java IO的各種流是阻塞的。 這意味著,當(dāng)一個(gè)線程調(diào)用read() 或write()時(shí),該線程被阻塞,直到有數(shù)據(jù)被讀取或者數(shù)據(jù)寫入。 該線程在阻塞期間不能做其他事情。 而Java NIO的非阻塞模式,如果通道沒有東西可讀,或不可寫,讀寫函數(shù)馬上返回,而不會阻塞,這個(gè)線程可以去做別的事情。 線程通常將非阻塞IO的空閑時(shí)間用于在其它通道上執(zhí)行IO操作,所以一個(gè)單獨(dú)的線程可以管理多個(gè)輸入和輸出通道(channel),即IO多路復(fù)用的原理。

零拷貝

在傳統(tǒng)的文件IO操作中,我們都是調(diào)用操作系統(tǒng)提供的底層標(biāo)準(zhǔn)IO系統(tǒng)調(diào)用函數(shù)read()、write() ,此時(shí)調(diào)用此函數(shù)的進(jìn)程(在JAVA中即java進(jìn)程)由當(dāng)前的用戶態(tài)切換到內(nèi)核態(tài),然后OS的內(nèi)核代碼負(fù)責(zé)將相應(yīng)的文件數(shù)據(jù)讀取到內(nèi)核的IO緩沖區(qū),然后再把數(shù)據(jù)從內(nèi)核IO緩沖區(qū)拷貝到進(jìn)程的私有地址空間中去,這樣便完成了一次IO操作。

資深架構(gòu)師總結(jié):徹底搞懂NIO效率高的原理

 

而NIO的零拷貝與傳統(tǒng)的文件IO操作最大的不同之處就在于它雖然也是要從磁盤讀取數(shù)據(jù),但是它并不需要將數(shù)據(jù)讀取到OS內(nèi)核緩沖區(qū),而是直接將進(jìn)程的用戶私有地址空間中的一部分區(qū)域與文件對象建立起映射關(guān)系,這樣直接從內(nèi)存中讀寫文件,速度大幅度提升。

資深架構(gòu)師總結(jié):徹底搞懂NIO效率高的原理

 

詳細(xì)的解析,之后會有單獨(dú)的博客進(jìn)行講解

NIO的核心部分

Java NIO主要由以下三個(gè)核心部分組成:

  • Channel
  • Buffer
  • Selector

Channel

基本上,所有的IO在NIO中都從一個(gè)Channel開始。 數(shù)據(jù)可以從Channel讀到Buffer中,也可以從Buffer寫到Channel中。 這里有個(gè)圖示:

資深架構(gòu)師總結(jié):徹底搞懂NIO效率高的原理

 

Channel和Buffer有好幾種類型。 下面是Java NIO中的一些主要Channel的實(shí)現(xiàn):

  • FileChannel(file)
  • DatagramChannel(UDP)
  • SocketChannel(TCP)
  • ServerSocketChannel(TCP)

這些通道涵蓋了UDP和TCP網(wǎng)絡(luò)IO以及文件IO。

資深架構(gòu)師總結(jié):徹底搞懂NIO效率高的原理

 

最后兩個(gè)channel的關(guān)系。 通過 ServerSocketChannel.accept() 方法監(jiān)聽新進(jìn)來的連接。 當(dāng) accept()方法返回的時(shí)候,它返回一個(gè)包含新進(jìn)來的連接的 SocketChannel。 因此, accept()方法會一直阻塞到有新連接到達(dá)。 通常不會僅僅只監(jiān)聽一個(gè)連接,在while循環(huán)中調(diào)用 accept()方法.

//打開 ServerSocketChannel

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

serverSocketChannel.socket().bind(new InetSocketAddress(9999));

while(true){

SocketChannel socketChannel = serverSocketChannel.accept();

//do something with socketChannel...

}

//關(guān)閉ServerSocketChannel

serverSocketChannel.close();

Buffer

緩沖區(qū)本質(zhì)上是一塊可以寫入數(shù)據(jù),然后可以從中讀取數(shù)據(jù)的內(nèi)存。 這塊內(nèi)存被包裝成NIO Buffer對象,并提供了一組方法,用來方便的訪問該塊內(nèi)存。

資深架構(gòu)師總結(jié):徹底搞懂NIO效率高的原理

 

Java NIO里關(guān)鍵的Buffer實(shí)現(xiàn):

  • ByteBuffer
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer

這些Buffer覆蓋了你能通過IO發(fā)送的基本數(shù)據(jù)類型: byte、short、int、long、float、double和char。

為了理解Buffer的工作原理,需要熟悉它的三個(gè)屬性:

  • capacity
  • position
  • limit

position和limit的含義取決于Buffer處在讀模式還是寫模式。 不管Buffer處在什么模式,capacity的含義總是一樣的。

資深架構(gòu)師總結(jié):徹底搞懂NIO效率高的原理

 

capacity

作為一個(gè)內(nèi)存塊,Buffer有個(gè)固定的最大值,就是capacity。 Buffer只能寫capacity個(gè)byte、long、char等類型。 一旦Buffer滿了,需要將其清空(通過讀數(shù)據(jù)或者清除數(shù)據(jù))才能繼續(xù)寫數(shù)據(jù)往里寫數(shù)據(jù)。

position

當(dāng)寫數(shù)據(jù)到Buffer中時(shí),position表示當(dāng)前的位置。 初始的position值為0。 當(dāng)一個(gè)byte、long等數(shù)據(jù)寫到Buffer后, position會向前移動(dòng)到下一個(gè)可插入數(shù)據(jù)的Buffer單元。 position最大可為capacity – 1.

當(dāng)讀取數(shù)據(jù)時(shí),也是從某個(gè)特定位置讀。 當(dāng)將Buffer從寫模式切換到讀模式,position會被重置為0。 當(dāng)從Buffer的position處讀取數(shù)據(jù)時(shí),position向前移動(dòng)到下一個(gè)可讀的位置。

資深架構(gòu)師總結(jié):徹底搞懂NIO效率高的原理

 

limit

在寫模式下,Buffer的limit表示最多能往Buffer里寫多少數(shù)據(jù)。 寫模式下,limit等于capacity。

當(dāng)切換Buffer到讀模式時(shí), limit表示你最多能讀到多少數(shù)據(jù)。 因此,當(dāng)切換Buffer到讀模式時(shí),limit會被設(shè)置成寫模式下的position值。

Selector

Selector允許單線程處理多個(gè) Channel。 如果你的應(yīng)用打開了多個(gè)連接(通道),但每個(gè)連接的流量都很低,使用Selector就會很方便。 例如,在一個(gè)聊天服務(wù)器中。

這是在一個(gè)單線程中使用一個(gè)Selector處理3個(gè)Channel的圖示:

資深架構(gòu)師總結(jié):徹底搞懂NIO效率高的原理

 

要使用Selector,得向Selector注冊Channel,然后調(diào)用它的select()方法。 這個(gè)方法會一直阻塞到某個(gè)注冊的通道有事件就緒。 一旦這個(gè)方法返回,線程就可以處理這些事件,事件例如有新連接進(jìn)來,數(shù)據(jù)接收等。

NIO與epoll的關(guān)系

Java NIO根據(jù)操作系統(tǒng)不同, 針對NIO中的Selector有不同的實(shí)現(xiàn):

  • macosx:KQueueSelectorProvider
  • solaris:DevPollSelectorProvider
  • linux:EPollSelectorProvider (Linux kernels >= 2.6)或PollSelectorProvider
  • windows:WindowsSelectorProvider

所以不需要特別指定,Oracle JDK會自動(dòng)選擇合適的Selector。 如果想設(shè)置特定的Selector,可以設(shè)置屬性,例如:

-Djava.nio.channels.spi.SelectorProvider=sun.nio.ch.EPollSelectorProvider

JDK在Linux已經(jīng)默認(rèn)使用epoll方式,但是JDK的epoll采用的是水平觸發(fā),所以Netty自4.0.16起, Netty為Linux通過JNI的方式提供了native socket transport。 Netty重新實(shí)現(xiàn)了epoll機(jī)制,

  1. 采用邊緣觸發(fā)方式
  2. netty epoll transport暴露了更多的nio沒有的配置參數(shù),如 TCP_CORK, SO_REUSEADDR等等。
  3. C代碼,更少GC,更少synchronized

使用native socket transport的方法很簡單,只需將相應(yīng)的類替換即可。

NioEventLoopGroup → EpollEventLoopGroup
NioEventLoop → EpollEventLoop
NIOServerSocketChannel → EpollServerSocketChannel
NioSocketChannel → EpollSocketChannel

NIO處理消息的核心思路

結(jié)合示例代碼,總結(jié)NIO的核心思路:

  1. NIO 模型中通常會有兩個(gè)線程,每個(gè)線程綁定一個(gè)輪詢器 selector ,在上面例子中serverSelector負(fù)責(zé)輪詢是否有新的連接,clientSelector負(fù)責(zé)輪詢連接是否有數(shù)據(jù)可讀
  2. 服務(wù)端監(jiān)測到新的連接之后,不再創(chuàng)建一個(gè)新的線程,而是直接將新連接綁定到clientSelector上,這樣就不用BIO模型中1w 個(gè)while循環(huán)在阻塞,參見(1)
  3. clientSelector被一個(gè) while 死循環(huán)包裹著,如果在某一時(shí)刻有多條連接有數(shù)據(jù)可讀,那么通過clientSelector.select(1)方法可以輪詢出來,進(jìn)而批量處理,參見(2)
  4. 數(shù)據(jù)的讀寫面向 Buffer,參見(3)

NIO的示例代碼

public class NIOServer {
 public static void main(String[] args) throws IOException {
 Selector serverSelector = Selector.open();
 Selector clientSelector = Selector.open();
 new Thread(() -> {
 try {
 // 對應(yīng)IO編程中服務(wù)端啟動(dòng)
 ServerSocketChannel listenerChannel = ServerSocketChannel.open();
 listenerChannel.socket().bind(new InetSocketAddress(8000));
 listenerChannel.configureBlocking(false);
 listenerChannel.register(serverSelector, SelectionKey.OP_ACCEPT);
 while (true) {
 // 監(jiān)測是否有新的連接,這里的1指的是阻塞的時(shí)間為 1ms
 if (serverSelector.select(1) > 0) {
 Set<SelectionKey> set = serverSelector.selectedKeys();
 Iterator<SelectionKey> keyIterator = set.iterator();
 while (keyIterator.hasNext()) {
 SelectionKey key = keyIterator.next();
 if (key.isAcceptable()) {
 try {
 // (1) 每來一個(gè)新連接,不需要?jiǎng)?chuàng)建一個(gè)線程,而是直接注冊到clientSelector
 SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept();
 clientChannel.configureBlocking(false);
 clientChannel.register(clientSelector, SelectionKey.OP_READ);
 } finally {
 keyIterator.remove();
 }
 }
 }
 }
 }
 } catch (IOException ignored) {
 }
 }).start();
 new Thread(() -> {
 try {
 while (true) {
 // (2) 批量輪詢是否有哪些連接有數(shù)據(jù)可讀,這里的1指的是阻塞的時(shí)間為 1ms
 if (clientSelector.select(1) > 0) {
 Set<SelectionKey> set = clientSelector.selectedKeys();
 Iterator<SelectionKey> keyIterator = set.iterator();
 while (keyIterator.hasNext()) {
 SelectionKey key = keyIterator.next();
 if (key.isReadable()) {
 try {
 SocketChannel clientChannel = (SocketChannel) key.channel();
 ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
 // (3) 面向 Buffer
 clientChannel.read(byteBuffer);
 byteBuffer.flip();
 System.out.println(Charset.defaultCharset().newDecoder().decode(byteBuffer)
 .toString());
 } finally {
 keyIterator.remove();
 key.interestOps(SelectionKey.OP_READ);
 }
 }
 }
 }
 }
 } catch (IOException ignored) {
 }
 }).start();
 }
}

分享到:
標(biāo)簽:架構(gòu) 效率 NIO
用戶無頭像

網(wǎng)友整理

注冊時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定