成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

Java NIO淺析

yeooo / 1722人閱讀

摘要:阻塞請(qǐng)求結(jié)果返回之前,當(dāng)前線程被掛起。也就是說(shuō)在異步中,不會(huì)對(duì)用戶線程產(chǎn)生任何阻塞。當(dāng)前線程在拿到此次請(qǐng)求結(jié)果的過(guò)程中,可以做其它事情。事實(shí)上,可以只用一個(gè)線程處理所有的通道。

準(zhǔn)備知識(shí) 同步、異步、阻塞、非阻塞

同步和異步說(shuō)的是服務(wù)端消息的通知機(jī)制,阻塞和非阻塞說(shuō)的是客戶端線程的狀態(tài)。
已客戶端一次網(wǎng)絡(luò)請(qǐng)求為例做簡(jiǎn)單說(shuō)明:

同步
同步是指一次請(qǐng)求沒(méi)有得到結(jié)果之前就不返回。

異步
請(qǐng)求不會(huì)立刻得到最終結(jié)果,服務(wù)器處理完成再異步通知客戶端。

阻塞
請(qǐng)求結(jié)果返回之前,當(dāng)前線程被掛起。在此期間不能做任何其他的事情。

非阻塞
請(qǐng)求立即返回,后續(xù)由客戶端時(shí)不時(shí)的詢問(wèn)服務(wù)器結(jié)果或者服務(wù)器異步回調(diào)。

同步IO、異步IO、阻塞IO、非阻塞IO

通常來(lái)說(shuō),IO操作包括:對(duì)硬盤的讀寫、對(duì)socket的讀寫以及外設(shè)的讀寫。
已一個(gè)IO讀取過(guò)程為例做簡(jiǎn)要說(shuō)明(如圖):

DMA把數(shù)據(jù)讀取到內(nèi)核空間的緩沖區(qū)(讀就緒)

內(nèi)核將數(shù)據(jù)拷貝到用戶空間。

內(nèi)核空間是用戶代碼無(wú)法控制的,所以用戶空間在讀取之前,首先會(huì)判斷是否已經(jīng)讀就緒。

同步IO
當(dāng)用戶發(fā)出IO請(qǐng)求操作之后,內(nèi)核會(huì)去查看要讀取的數(shù)據(jù)是否就緒,如果數(shù)據(jù)沒(méi)有就緒,就一直等待。需要通過(guò)用戶線程或者內(nèi)核不斷地去輪詢數(shù)據(jù)是否就緒,當(dāng)數(shù)據(jù)就緒時(shí),再將數(shù)據(jù)從內(nèi)核拷貝到用戶空間。

異步IO
只有IO請(qǐng)求操作的發(fā)出是由用戶線程來(lái)進(jìn)行的,IO操作的兩個(gè)階段都是由內(nèi)核自動(dòng)完成,然后發(fā)送通知告知用戶線程IO操作已經(jīng)完成。也就是說(shuō)在異步IO中,不會(huì)對(duì)用戶線程產(chǎn)生任何阻塞。

阻塞IO
當(dāng)用戶線程發(fā)起一個(gè)IO請(qǐng)求操作(以讀請(qǐng)求操作為例),內(nèi)核查看要讀取的數(shù)據(jù)還沒(méi)就緒,當(dāng)前線程被掛起,阻塞等待結(jié)果返回。

非阻塞IO
如果數(shù)據(jù)沒(méi)有就緒,則會(huì)返回一個(gè)標(biāo)志信息告知用戶線程當(dāng)前要讀的數(shù)據(jù)沒(méi)有就緒。當(dāng)前線程在拿到此次請(qǐng)求結(jié)果的過(guò)程中,可以做其它事情。

JAVA中的BIO、NIO、AIO

BIO
同步阻塞,傳統(tǒng)io方式。
適用于連接數(shù)目比較小且固定的架構(gòu),這種方式對(duì)服務(wù)器資源要求比較高,并發(fā)局限于應(yīng)用中。

NIO
同步非阻塞,jdk4開(kāi)始支持。
適用于連接數(shù)目多且連接比較短(輕操作)的架構(gòu),比如聊天服務(wù)器。

AIO
異步非阻塞,jdk7開(kāi)始支持。
適用于連接數(shù)目多且連接比較長(zhǎng)(重操作)的架構(gòu)。

形象的理解NIO和AIO:
如果把內(nèi)核比作快遞,NIO就是你要自己時(shí)不時(shí)到官網(wǎng)查下快遞是否已經(jīng)到了你所在城市,然后自己去取快遞;AIO就是快遞員送貨上門了。

Linux下五種IO模型

阻塞I/O(blocking I/O)

非阻塞I/O (nonblocking I/O)

I/O復(fù)用(select 和poll) (I/O multiplexing)

信號(hào)驅(qū)動(dòng)I/O (signal driven I/O (SIGIO))

異步I/O (asynchronous I/O (the POSIX aio_functions))

IO復(fù)用模型(IO多路復(fù)用)

簡(jiǎn)言之,就是通過(guò)單個(gè)線程(進(jìn)程)來(lái)管理多IO流。如圖:

IO多路復(fù)用避免阻塞在IO上,原本為多進(jìn)程或多線程來(lái)接收多個(gè)連接的消息變?yōu)閱芜M(jìn)程或單線程保存多個(gè)socket的狀態(tài)后輪詢處理。只有當(dāng)某個(gè)socket讀寫就緒后,才真正調(diào)用實(shí)際的IO讀寫操作。這樣可以避免線程切換帶來(lái)的開(kāi)銷。

實(shí)現(xiàn)IO多路復(fù)用需要函數(shù)來(lái)支持,就是你說(shuō)的linux下的select、poll、epoll以及win下 iocp和BSD的kqueue。這幾個(gè)函數(shù)也會(huì)使進(jìn)程阻塞,但是和阻塞I/O所不同的是,它可以同時(shí)阻塞多個(gè)I/O操作。而且可以同時(shí)對(duì)多個(gè)讀操作,多個(gè)寫操作的I/O準(zhǔn)備狀態(tài)進(jìn)行檢測(cè)。

IO多路復(fù)用為何比非阻塞IO模型的效率高是因?yàn)樵诜亲枞鸌O中,不斷地詢問(wèn)socket狀態(tài)是通過(guò)用戶線程去進(jìn)行的,而在IO多路復(fù)用中,輪詢每個(gè)socket狀態(tài)是內(nèi)核在進(jìn)行的,這個(gè)效率要比用戶線程要高的多。

五種IO模型以及select、poll、epoll的詳細(xì)介紹推薦大家看這篇文章
socket阻塞與非阻塞,同步與異步、I/O模型

理解Reactor和Proactor模式

在Reactor模式中,會(huì)先對(duì)每個(gè)client注冊(cè)感興趣的事件,然后有一個(gè)線程專門去輪詢每個(gè)client是否有事件發(fā)生,當(dāng)有事件發(fā)生時(shí)(讀寫就緒),便順序處理每個(gè)事件,當(dāng)所有事件處理完之后,便再轉(zhuǎn)去繼續(xù)輪詢,如圖所示:

從這里可以看出,多路復(fù)用IO就是采用Reactor模式。注意,上面的圖中展示的是順序處理每個(gè)事件,當(dāng)然為了提高事件處理速度,可以通過(guò)多線程或者線程池的方式來(lái)處理事件。
在Proactor模式中,當(dāng)檢測(cè)到有事件發(fā)生時(shí),會(huì)新起一個(gè)異步操作,然后交由內(nèi)核線程去處理,當(dāng)內(nèi)核線程完成IO操作之后,發(fā)送一個(gè)通知告知操作已完成,可以得知,異步IO模型采用的就是Proactor模式。

這部分摘選自:Java NIO:淺析I/O模型

Java NIO介紹

Channels and Buffers(通道和緩沖區(qū))
標(biāo)準(zhǔn)的IO基于字節(jié)流和字符流進(jìn)行操作的,而NIO是基于通道(Channel)和緩沖區(qū)(Buffer)進(jìn)行操作,數(shù)據(jù)總是從通道讀取到緩沖區(qū)中,或者從緩沖區(qū)寫入到通道中。

Non-blocking IO(非阻塞IO)
Java NIO可以讓你非阻塞的使用IO,例如:當(dāng)線程從通道讀取數(shù)據(jù)到緩沖區(qū)時(shí),線程還是可以進(jìn)行其他事情。當(dāng)數(shù)據(jù)被寫入到緩沖區(qū)時(shí),線程可以繼續(xù)處理它。從緩沖區(qū)寫入通道也類似。

Selectors(選擇器)
選擇器用于監(jiān)聽(tīng)多個(gè)通道的事件(比如:連接打開(kāi),數(shù)據(jù)到達(dá))。因此,單個(gè)的線程可以監(jiān)聽(tīng)多個(gè)數(shù)據(jù)通道。

NIO與IO區(qū)別

? IO ? ? ? ? ? ? ? ? NIO
面向流? ? ?? ? 面向緩沖
阻塞IO ? ? ?? ? 非阻塞IO
? 無(wú)? ? ? ? ? ? ? ? 選擇器

Channel

Java NIO的通道類似流,但又有些不同:

既可以從通道中讀取數(shù)據(jù),又可以寫數(shù)據(jù)到通道。但流的讀寫通常是單向的。

通道可以異步地讀寫。

通道中的數(shù)據(jù)總是要先讀到一個(gè)Buffer,或者總是要從一個(gè)Buffer中寫入。

Channel的實(shí)現(xiàn)

FileChannel (從文件中讀寫數(shù)據(jù))

DatagramChannel (通過(guò)UDP讀寫網(wǎng)絡(luò)中的數(shù)據(jù))

SocketChannel (通過(guò)TCP讀寫網(wǎng)絡(luò)中的數(shù)據(jù))

ServerSocketChannel (可以監(jiān)聽(tīng)新進(jìn)來(lái)的TCP連接,像Web服務(wù)器那樣)

Buffer

Java NIO中的Buffer用于和NIO通道進(jìn)行交互。如你所知,數(shù)據(jù)是從通道讀入緩沖區(qū),從緩沖區(qū)寫入到通道中的。

Buffer的基本用法

使用Buffer讀寫數(shù)據(jù)一般遵循以下四個(gè)步驟:

分配指定大小的buffer空間

寫入數(shù)據(jù)到Buffer

調(diào)用flip()方法

從Buffer中讀取數(shù)據(jù)

調(diào)用clear()方法或者compact()方法

當(dāng)向buffer寫入數(shù)據(jù)時(shí),buffer會(huì)記錄下寫了多少數(shù)據(jù)。一旦要讀取數(shù)據(jù),需要通過(guò)flip()方法將Buffer從寫模式切換到讀模式。在讀模式下,可以讀取之前寫入到buffer的所有數(shù)據(jù)。

一旦讀完了所有的數(shù)據(jù),就需要清空緩沖區(qū),讓它可以再次被寫入。有兩種方式能清空緩沖區(qū):調(diào)用clear()或compact()方法。clear()方法會(huì)清空整個(gè)緩沖區(qū)。compact()方法只會(huì)清除已經(jīng)讀過(guò)的數(shù)據(jù)。任何未讀的數(shù)據(jù)都被移到緩沖區(qū)的起始處,新寫入的數(shù)據(jù)將放到緩沖區(qū)未讀數(shù)據(jù)的后面。
注:Buffer中的數(shù)據(jù)并未清除,只是這些標(biāo)記告訴我們可以從哪里開(kāi)始往Buffer里寫數(shù)據(jù)。

Buffer的類型

ByteBuffer

MappedByteBuffer

CharBuffer

DoubleBuffer

FloatBuffer

IntBuffer

LongBuffer

ShortBuffer

Selector

Selector(選擇器)是Java NIO中能夠檢測(cè)一到多個(gè)通道,并能夠知曉通道是否為諸如讀寫事件做好準(zhǔn)備的組件。這樣,一個(gè)多帶帶的線程可以管理多個(gè)channel,從而管理多個(gè)網(wǎng)絡(luò)連接。

為什么使用Selector?

僅用單個(gè)線程來(lái)處理多個(gè)Channels的好處是,只需要更少的線程來(lái)處理通道。事實(shí)上,可以只用一個(gè)線程處理所有的通道。對(duì)于操作系統(tǒng)來(lái)說(shuō),線程之間上下文切換的開(kāi)銷很大,而且每個(gè)線程都要占用系統(tǒng)的一些資源(如內(nèi)存)。因此,使用的線程越少越好。

但是,需要記住,現(xiàn)代的操作系統(tǒng)和CPU在多任務(wù)方面表現(xiàn)的越來(lái)越好,所以多線程的開(kāi)銷隨著時(shí)間的推移,變得越來(lái)越小了。實(shí)際上,如果一個(gè)CPU有多個(gè)內(nèi)核,不使用多任務(wù)可能是在浪費(fèi)CPU能力。不管怎么說(shuō),關(guān)于那種設(shè)計(jì)的討論應(yīng)該放在另一篇不同的文章中。在這里,只要知道使用Selector能夠處理多個(gè)通道就足夠了。

NIO如何實(shí)現(xiàn)非阻塞?

服務(wù)器上所有Channel需要向Selector注冊(cè),而Selector則負(fù)責(zé)監(jiān)視這些Socket的IO狀態(tài)(觀察者),當(dāng)其中任意一個(gè)或者多個(gè)Channel具有可用的IO操作時(shí),該Selector的select()方法將會(huì)返回大于0的整數(shù),該整數(shù)值就表示該Selector上有多少個(gè)Channel具有可用的IO操作,并提供了selectedKeys()方法來(lái)返回這些Channel對(duì)應(yīng)的SelectionKey集合(一個(gè)SelectionKey對(duì)應(yīng)一個(gè)就緒的通道)。正是通過(guò)Selector,使得服務(wù)器端只需要不斷地調(diào)用Selector實(shí)例的select()方法即可知道當(dāng)前所有Channel是否有需要處理的IO操作。
注:java NIO就是多路復(fù)用IO,jdk7之后底層是epoll模型。

一個(gè)簡(jiǎn)單的demo
/**
 * NioServer
 * Date: 6/27/2016
 * Time: 8:06 PM
 *
 * @author xiaodong.fan
 */
public class NioServer {

  public static void main(String[] args) throws Exception {
    // 1、初始化一個(gè)ServerSocketChannel
    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 9999);
    serverSocketChannel.configureBlocking(false);// 設(shè)置為非阻塞模式,后續(xù)的accept()方法會(huì)立刻返回
    serverSocketChannel.socket().bind(inetSocketAddress, 1024);// 監(jiān)聽(tīng)本地9999端口的請(qǐng)求,第二個(gè)參數(shù)限制可以建立的最大連接數(shù)
    Selector selector = Selector.open();
    /**
     * 將通道注冊(cè)到一個(gè)選擇器上(非阻塞模式與選擇器搭配會(huì)工作的更好)
     * 注意register()方法的第二個(gè)參數(shù)。這是一個(gè)“interest集合”,意思是在通過(guò)Selector監(jiān)聽(tīng)Channel時(shí)對(duì)什么事件感興趣。
     * 可以監(jiān)聽(tīng)四種不同類型的事件:OP_CONNECT,OP_ACCEPT,OP_READ,OP_WRITE
     * 如果你對(duì)不止一種事件感興趣,那么可以用“位或”操作符將常量連接起來(lái):SelectionKey.OP_READ | SelectionKey.OP_WRITE
     */
    serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

    // 2、監(jiān)聽(tīng)連接請(qǐng)求并處理
    while (true) {
      int connects = selector.select(2000);// 每次最多阻塞2秒

      if (connects == 0) {
        System.out.println("沒(méi)有請(qǐng)求...");
        continue;
      } else {
        System.out.println("請(qǐng)求來(lái)了...");
      }

      // 獲取監(jiān)聽(tīng)到有連接請(qǐng)求的channel對(duì)應(yīng)的selectionKey
      Set selectedKeys = selector.selectedKeys();
      // 遍歷selectionKey來(lái)訪問(wèn)就緒的通道
      Iterator selectedKeyIterator = selectedKeys.iterator();
      while (selectedKeyIterator.hasNext()) {
        SelectionKey selectionKey = selectedKeyIterator.next();
        if (selectionKey.isValid()) {

          if (selectionKey.isAcceptable()) {// 接收就緒
            ServerSocketChannel channel = (ServerSocketChannel) selectionKey.channel();
            // 返回一個(gè)包含新進(jìn)來(lái)的連接SocketChannel,因?yàn)榍懊嬖O(shè)置的非阻塞模式,這里會(huì)立即返回。
            SocketChannel socketChannel = channel.accept();
            if (socketChannel == null) {
              return;
            }
            socketChannel.configureBlocking(false);
            socketChannel.register(selector, SelectionKey.OP_READ);
            System.out.println("連接建立完成");
            doWrite(socketChannel, "connection is established");// 連接建立完成,給客戶端發(fā)消息

          } else if (selectionKey.isReadable()) {// 讀就緒

            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
            ByteBuffer readBuffer = ByteBuffer.allocate(10);
            while ((socketChannel.read(readBuffer)) > 0) {// // 讀取客戶端發(fā)送來(lái)的消息
              readBuffer.flip();
              byte[] bytes = new byte[readBuffer.remaining()];
              readBuffer.get(bytes);
              String body = new String(bytes, "utf-8");
              doWrite(socketChannel, body);// 將客戶端發(fā)送的內(nèi)容原封不動(dòng)的發(fā)回去
              readBuffer.clear();
            }
            socketChannel.close();//讀取數(shù)據(jù)完畢后關(guān)閉連接,如果不關(guān)閉一直處于連接狀態(tài)。

          }
        }

        selectedKeyIterator.remove(); // 注意每次必須手動(dòng)remove(),下次該通道變成就緒時(shí),Selector會(huì)再次將其放入已選擇鍵集中
      }
    }
  }

  private static void doWrite(SocketChannel socketChannel, String response) throws IOException {
    if (StringUtils.isNotBlank(response)) {
      byte[] bytes = response.getBytes();
      ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
      writeBuffer.put(bytes);
      writeBuffer.flip();
      // 發(fā)送消息到客戶端
      socketChannel.write(writeBuffer);
      writeBuffer.clear();
    }

  }

}
參考文章

Java NIO:淺析I/O模型
socket阻塞與非阻塞,同步與異步、I/O模型
Java BIO、NIO、AIO 學(xué)習(xí)
Java NIO:NIO概述
Java NIO 系列教程
Java網(wǎng)絡(luò)編程——使用NIO實(shí)現(xiàn)非阻塞Socket通信

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/64851.html

相關(guān)文章

  • Asyncdb(一):寫一個(gè)純函數(shù)式的Mysql異步驅(qū)動(dòng)

    摘要:相信你們也注意到了,但是應(yīng)該大多數(shù)人都不是很熟悉,它也是一個(gè)基于,使用編寫的,完全異步的數(shù)據(jù)庫(kù)驅(qū)動(dòng),同時(shí)支持和,其項(xiàng)目地址。 之前的Akka系列博客接下去可能并不會(huì)經(jīng)常更新了,但是后續(xù)看到一些好的點(diǎn)或者大家對(duì)哪些還是比較感興趣還會(huì)繼續(xù)寫幾篇,這里先跟大家說(shuō)明一下。 背景 寫一個(gè)純函數(shù)式的Mysql異步驅(qū)動(dòng)這個(gè)構(gòu)思是公司的一個(gè)大佬提的,這將會(huì)是一個(gè)開(kāi)源項(xiàng)目,我也很有幸能夠參與其中,嘗試寫...

    chaosx110 評(píng)論0 收藏0
  • 關(guān)于Java IO與NIO知識(shí)都在這里

    摘要:從通道進(jìn)行數(shù)據(jù)寫入創(chuàng)建一個(gè)緩沖區(qū),填充數(shù)據(jù),并要求通道寫入數(shù)據(jù)。三之通道主要內(nèi)容通道介紹通常來(lái)說(shuō)中的所有都是從通道開(kāi)始的。從中選擇選擇器維護(hù)注冊(cè)過(guò)的通道的集合,并且這種注冊(cè)關(guān)系都被封裝在當(dāng)中停止選擇的方法方法和方法。 由于內(nèi)容比較多,我下面放的一部分是我更新在我的微信公眾號(hào)上的鏈接,微信排版比較好看,更加利于閱讀。每一篇文章下面我都把文章的主要內(nèi)容給列出來(lái)了,便于大家學(xué)習(xí)與回顧。 Ja...

    Riddler 評(píng)論0 收藏0
  • Java深入-框架技巧

    摘要:從使用到原理學(xué)習(xí)線程池關(guān)于線程池的使用,及原理分析分析角度新穎面向切面編程的基本用法基于注解的實(shí)現(xiàn)在軟件開(kāi)發(fā)中,分散于應(yīng)用中多出的功能被稱為橫切關(guān)注點(diǎn)如事務(wù)安全緩存等。 Java 程序媛手把手教你設(shè)計(jì)模式中的撩妹神技 -- 上篇 遇一人白首,擇一城終老,是多么美好的人生境界,她和他歷經(jīng)風(fēng)雨慢慢變老,回首走過(guò)的點(diǎn)點(diǎn)滴滴,依然清楚的記得當(dāng)初愛(ài)情萌芽的模樣…… Java 進(jìn)階面試問(wèn)題列表 -...

    chengtao1633 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<