欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java BIO,NIO,AIO總結(jié)

 更新時(shí)間:2020年09月09日 10:20:57   作者:崔笑顏  
這篇文章主要介紹了Java BIO,NIO,AIO的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)Java,感興趣的朋友可以了解下

Java 中的 BIO、NIO和 AIO 理解為是 Java 語(yǔ)言對(duì)操作系統(tǒng)的各種 IO 模型的封裝。程序員在使用這些 API 的時(shí)候,不需要關(guān)心操作系統(tǒng)層面的知識(shí),也不需要根據(jù)不同操作系統(tǒng)編寫(xiě)不同的代碼。只需要使用Java的API就可以了。

在講 BIO,NIO,AIO 之前先來(lái)回顧一下這樣幾個(gè)概念:同步與異步,阻塞與非阻塞。 同步與異步

  • 同步: 同步就是發(fā)起一個(gè)調(diào)用后,被調(diào)用者未處理完請(qǐng)求之前,調(diào)用不返回。
  • 異步: 異步就是發(fā)起一個(gè)調(diào)用后,立刻得到被調(diào)用者的回應(yīng)表示已接收到請(qǐng)求,但是被調(diào)用者并沒(méi)有返回結(jié)果,此時(shí)我們可以處理其他的請(qǐng)求,被調(diào)用者通常依靠事件,回調(diào)等機(jī)制來(lái)通知調(diào)用者其返回結(jié)果。 同步和異步的區(qū)別最大在于異步的話調(diào)用者不需要等待處理結(jié)果,被調(diào)用者會(huì)通過(guò)回調(diào)等機(jī)制來(lái)通知調(diào)用者其返回結(jié)果。

阻塞和非阻塞

  • 阻塞: 阻塞就是發(fā)起一個(gè)請(qǐng)求,調(diào)用者一直等待請(qǐng)求結(jié)果返回,也就是當(dāng)前線程會(huì)被掛起,無(wú)法從事其他任務(wù),只有當(dāng)條件就緒才能繼續(xù)。
  • 非阻塞: 非阻塞就是發(fā)起一個(gè)請(qǐng)求,調(diào)用者不用一直等著結(jié)果返回,可以先去干其他事情。 舉個(gè)生活中簡(jiǎn)單的例子,你媽媽讓你燒水,小時(shí)候你比較笨啊,在那里傻等著水開(kāi)(同步阻塞)。等你稍微再長(zhǎng)大一點(diǎn),你知道每次燒水的空隙可以去干點(diǎn)其他事,然后只需要時(shí)不時(shí)來(lái)看看水開(kāi)了沒(méi)有(同步非阻塞)。后來(lái),你們家用上了水開(kāi)了會(huì)發(fā)出聲音的壺,這樣你就只需要聽(tīng)到響聲后就知道水開(kāi)了,在這期間你可以隨便干自己的事情,你需要去倒水了(異步非阻塞)。

BIO (Blocking I/O)

同步阻塞I/O模式,數(shù)據(jù)的讀取寫(xiě)入必須阻塞在一個(gè)線程內(nèi)等待其完成。

傳統(tǒng) BIO

BIO通信(一請(qǐng)求一應(yīng)答)模型圖如下(圖源網(wǎng)絡(luò),原出處不明):

采用 BIO 通信模型 的服務(wù)端,通常由一個(gè)獨(dú)立的 Acceptor 線程負(fù)責(zé)監(jiān)聽(tīng)客戶端的連接。我們一般通過(guò)在while(true) 循環(huán)中服務(wù)端會(huì)調(diào)用 accept() 方法等待接收客戶端的連接的方式監(jiān)聽(tīng)請(qǐng)求,請(qǐng)求一旦接收到一個(gè)連接請(qǐng)求,就可以建立通信套接字在這個(gè)通信套接字上進(jìn)行讀寫(xiě)操作,此時(shí)不能再接收其他客戶端連接請(qǐng)求,只能等待同當(dāng)前連接的客戶端的操作執(zhí)行完成, 不過(guò)可以通過(guò)多線程來(lái)支持多個(gè)客戶端的連接,如上圖所示。

如果要讓 BIO 通信模型 能夠同時(shí)處理多個(gè)客戶端請(qǐng)求,就必須使用多線程(主要原因是socket.accept()、socket.read()、socket.write() 涉及的三個(gè)主要函數(shù)都是同步阻塞的),也就是說(shuō)它在接收到客戶端連接請(qǐng)求之后為每個(gè)客戶端創(chuàng)建一個(gè)新的線程進(jìn)行鏈路處理,處理完成之后,通過(guò)輸出流返回應(yīng)答給客戶端,線程銷(xiāo)毀。這就是典型的 一請(qǐng)求一應(yīng)答通信模型 。我們可以設(shè)想一下如果這個(gè)連接不做任何事情的話就會(huì)造成不必要的線程開(kāi)銷(xiāo),不過(guò)可以通過(guò) 線程池機(jī)制 改善,線程池還可以讓線程的創(chuàng)建和回收成本相對(duì)較低。使用FixedThreadPool 可以有效的控制了線程的最大數(shù)量,保證了系統(tǒng)有限的資源的控制,實(shí)現(xiàn)了N(客戶端請(qǐng)求數(shù)量):M(處理客戶端請(qǐng)求的線程數(shù)量)的偽異步I/O模型(N 可以遠(yuǎn)遠(yuǎn)大于 M),下面一節(jié)"偽異步 BIO"中會(huì)詳細(xì)介紹到。

我們?cè)僭O(shè)想一下當(dāng)客戶端并發(fā)訪問(wèn)量增加后這種模型會(huì)出現(xiàn)什么問(wèn)題?

在 Java 虛擬機(jī)中,線程是寶貴的資源,線程的創(chuàng)建和銷(xiāo)毀成本很高,除此之外,線程的切換成本也是很高的。尤其在 Linux 這樣的操作系統(tǒng)中,線程本質(zhì)上就是一個(gè)進(jìn)程,創(chuàng)建和銷(xiāo)毀線程都是重量級(jí)的系統(tǒng)函數(shù)。如果并發(fā)訪問(wèn)量增加會(huì)導(dǎo)致線程數(shù)急劇膨脹可能會(huì)導(dǎo)致線程堆棧溢出、創(chuàng)建新線程失敗等問(wèn)題,最終導(dǎo)致進(jìn)程宕機(jī)或者僵死,不能對(duì)外提供服務(wù)。

偽異步 IO

為了解決同步阻塞I/O面臨的一個(gè)鏈路需要一個(gè)線程處理的問(wèn)題,后來(lái)有人對(duì)它的線程模型進(jìn)行了優(yōu)化一一一后端通過(guò)一個(gè)線程池來(lái)處理多個(gè)客戶端的請(qǐng)求接入,形成客戶端個(gè)數(shù)M:線程池最大線程數(shù)N的比例關(guān)系,其中M可以遠(yuǎn)遠(yuǎn)大于N.通過(guò)線程池可以靈活地調(diào)配線程資源,設(shè)置線程的最大值,防止由于海量并發(fā)接入導(dǎo)致線程耗盡。

偽異步IO模型圖(圖源網(wǎng)絡(luò),原出處不明):

采用線程池和任務(wù)隊(duì)列可以實(shí)現(xiàn)一種叫做偽異步的 I/O 通信框架,它的模型圖如上圖所示。當(dāng)有新的客戶端接入時(shí),將客戶端的 Socket 封裝成一個(gè)Task(該任務(wù)實(shí)現(xiàn)java.lang.Runnable接口)投遞到后端的線程池中進(jìn)行處理,JDK 的線程池維護(hù)一個(gè)消息隊(duì)列和 N 個(gè)活躍線程,對(duì)消息隊(duì)列中的任務(wù)進(jìn)行處理。由于線程池可以設(shè)置消息隊(duì)列的大小和最大線程數(shù),因此,它的資源占用是可控的,無(wú)論多少個(gè)客戶端并發(fā)訪問(wèn),都不會(huì)導(dǎo)致資源的耗盡和宕機(jī)。

偽異步I/O通信框架采用了線程池實(shí)現(xiàn),因此避免了為每個(gè)請(qǐng)求都創(chuàng)建一個(gè)獨(dú)立線程造成的線程資源耗盡問(wèn)題。不過(guò)因?yàn)樗牡讓尤匀皇峭阶枞腂IO模型,因此無(wú)法從根本上解決問(wèn)題。

代碼示例

下面代碼中演示了BIO通信(一請(qǐng)求一應(yīng)答)模型。我們會(huì)在客戶端創(chuàng)建多個(gè)線程依次連接服務(wù)端并向其發(fā)送"當(dāng)前時(shí)間+:hello world",服務(wù)端會(huì)為每個(gè)客戶端線程創(chuàng)建一個(gè)線程來(lái)處理。代碼示例出自閃電俠的博客,原地址如下:

客戶端

/**
 * 
 * @author 閃電俠
 * @date 2018年10月14日
 * @Description:客戶端
 */
public class IOClient {

 public static void main(String[] args) {
  // TODO 創(chuàng)建多個(gè)線程,模擬多個(gè)客戶端連接服務(wù)端
  new Thread(() -> {
   try {
    Socket socket = new Socket("127.0.0.1", 3333);
    while (true) {
     try {
      socket.getOutputStream().write((new Date() + ": hello world").getBytes());
      Thread.sleep(2000);
     } catch (Exception e) {
     }
    }
   } catch (IOException e) {
   }
  }).start();

 }

}

服務(wù)端

/**
 * @author 閃電俠
 * @date 2018年10月14日
 * @Description: 服務(wù)端
 */
public class IOServer {

 public static void main(String[] args) throws IOException {
  // TODO 服務(wù)端處理客戶端連接請(qǐng)求
  ServerSocket serverSocket = new ServerSocket(3333);

  // 接收到客戶端連接請(qǐng)求之后為每個(gè)客戶端創(chuàng)建一個(gè)新的線程進(jìn)行鏈路處理
  new Thread(() -> {
   while (true) {
    try {
     // 阻塞方法獲取新的連接
     Socket socket = serverSocket.accept();

     // 每一個(gè)新的連接都創(chuàng)建一個(gè)線程,負(fù)責(zé)讀取數(shù)據(jù)
     new Thread(() -> {
      try {
       int len;
       byte[] data = new byte[1024];
       InputStream inputStream = socket.getInputStream();
       // 按字節(jié)流方式讀取數(shù)據(jù)
       while ((len = inputStream.read(data)) != -1) {
        System.out.println(new String(data, 0, len));
       }
      } catch (IOException e) {
      }
     }).start();

    } catch (IOException e) {
    }

   }
  }).start();

 }

}

總結(jié)

在活動(dòng)連接數(shù)不是特別高(小于單機(jī)1000)的情況下,這種模型是比較不錯(cuò)的,可以讓每一個(gè)連接專注于自己的 I/O 并且編程模型簡(jiǎn)單,也不用過(guò)多考慮系統(tǒng)的過(guò)載、限流等問(wèn)題。線程池本身就是一個(gè)天然的漏斗,可以緩沖一些系統(tǒng)處理不了的連接或請(qǐng)求。但是,當(dāng)面對(duì)十萬(wàn)甚至百萬(wàn)級(jí)連接的時(shí)候,傳統(tǒng)的 BIO 模型是無(wú)能為力的。因此,我們需要一種更高效的 I/O 處理模型來(lái)應(yīng)對(duì)更高的并發(fā)量。

NIO (no blocking io 也叫 new io)

NIO 即非阻塞IO,是JDK 1.4 更新的api, 核心內(nèi)容是 將建立連接、數(shù)據(jù)可讀、可寫(xiě)等事件交給了操作系統(tǒng)來(lái)維護(hù), 通過(guò)調(diào)用操作系統(tǒng)的 api (如:select、epoll等),來(lái)判斷當(dāng)前是否支持:可讀、可寫(xiě),如果當(dāng)前不可操作,那么直接返回,從而實(shí)現(xiàn)了非阻塞。 而不需要像 BIO 那樣每次去輪詢等待連接的建立以及數(shù)據(jù)的準(zhǔn)備是否完成。主要核心的模塊分以下幾類:

1. 緩沖區(qū)Buffer

一個(gè)特定基類(byte、short、int、long 等)的數(shù)據(jù)容器,用作在建立socket 連接之后的數(shù)據(jù)傳輸。
通過(guò) capacity, limit, position,mark 指針來(lái)實(shí)現(xiàn)數(shù)據(jù)的讀寫(xiě)

get()、put() 方法為每個(gè)子類都具有的讀、寫(xiě)數(shù)據(jù)的api方法,當(dāng)從當(dāng)前的 position 讀或?qū)懙耐瑫r(shí),position會(huì)增加 相應(yīng)讀寫(xiě)的數(shù)據(jù)的長(zhǎng)度。當(dāng)position 達(dá)到limit 之后,再次 get、put則會(huì)拋出異常

2. Channel 連接通道

一個(gè) channel 代表一個(gè)與“實(shí)體”的連接通道,如:硬件設(shè)備、文件、網(wǎng)絡(luò) socket 。通過(guò)連接通道可以使得客戶端-服務(wù)器互相傳輸數(shù)據(jù),因此通道也是全雙工的(因?yàn)槭墙⒃赥CP 傳輸層的協(xié)議上,因此具備全雙工的能力)。

JDK 中 channel 可以分為以下幾類:

SelectableChannel 用于 阻塞和非阻塞 socket 連接的通道
FileChannel 用于文件操作,包括:reading, writing, mapping, and manipulating a file

3.Selector 多路復(fù)用選擇器

用于 SelectableChannel 的多路復(fù)用器,當(dāng)使用非阻塞的 socket 時(shí),需要將監(jiān)聽(tīng)的通道 SelectableChannel 感興趣的事件注冊(cè)到 selector 多路復(fù)用器上(selector 實(shí)際上是通過(guò)調(diào)用操作系統(tǒng)層面的 select、epoll 方法來(lái)獲取當(dāng)前可用的時(shí)間)

與之對(duì)應(yīng)的感興趣的事件用 SelectionKey 來(lái)表示

  • OP_READ = 1 << 0; 可讀
  • OP_WRITE = 1 << 2; 可寫(xiě)
  • OP_CONNECT = 1 << 3; // 完成連接
  • OP_ACCEPT = 1 << 4; // 接收連接

處理流程圖:

代碼示例:

  1. 通過(guò) ServerSocketChannel 監(jiān)聽(tīng) 8082 端口
  2. 設(shè)置為非阻塞
  3. 選擇與操作系統(tǒng)適配的選擇器,serverSocketChannel 的 OP_ACCEPT 事件注冊(cè)到 selector 選擇器上
  4. 當(dāng)OP_ACCEPT 事件觸發(fā)時(shí),將所有建立好的Socketchannel 連接的感興趣的事件(這里為 read事件)再次注冊(cè)到Selector 上
    // 1.根據(jù)操作系統(tǒng)選擇適當(dāng)?shù)牡讓?io復(fù)用方法
    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    serverSocketChannel.bind(new InetSocketAddress(8082));
    //2.設(shè)置為非阻塞
    serverSocketChannel.configureBlocking(false);
    //3.選擇與操作系統(tǒng)適配的選擇器
    Selector selector = Selector.open();
    //將 serverSocket 的OP_ACCEPT 事件注冊(cè)到 selector 選擇器上
    serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    while (true) {
      // 4.監(jiān)聽(tīng)當(dāng)前連接建立情況
      int select = selector.select();
      if (select > 0) {
        //判斷連接業(yè)務(wù)類型
        Set<SelectionKey> set = selector.selectedKeys();
        Iterator<SelectionKey> iterator = set.iterator();
        while (iterator.hasNext()) {
          SelectionKey key = iterator.next();
          iterator.remove();
          //建立連接
          if (key.isAcceptable()) {
            ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
            //通過(guò) accept 方法獲取與 server端 已經(jīng)創(chuàng)建好的 socket連接
            SocketChannel sc = ssc.accept();
            //設(shè)置為非阻塞
            sc.configureBlocking(false);
            //注冊(cè)感興趣的事件為 READ
            sc.register(selector, SelectionKey.OP_READ);
          }
          //可讀
          else if (key.isReadable()) {
            SocketChannel socket = (SocketChannel) key.channel();
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            socket.read(byteBuffer);
            System.out.println(new String(byteBuffer.array(), StandardCharsets.UTF_8));
            key.interestOps(SelectionKey.OP_WRITE);
          }
          //可寫(xiě)
          else if (key.isWritable()) {
            SocketChannel socket = (SocketChannel) key.channel();
            socket.write(ByteBuffer.wrap("I'm receive your message".getBytes(StandardCharsets.UTF_8)));
            socket.close();
            System.out.println("連接關(guān)閉成功!");
          }
        }
      }
    }

AIO(asynchronous io)

NIO 2.0引入了新的異步通道的概念,并提供了異步文件通道和異步套接字通道的實(shí)現(xiàn)。
異步的套接字通道時(shí)真正的異步非阻塞I/O,對(duì)應(yīng)于UNIX網(wǎng)絡(luò)編程中的事件驅(qū)動(dòng)I/O(AIO)。他不需要過(guò)多的Selector對(duì)注冊(cè)的通道進(jìn)行輪詢即可實(shí)現(xiàn)異步讀寫(xiě),從而簡(jiǎn)化了NIO的編程模型。

代碼示例

  private static void server() throws IOException {
    //根據(jù)操作系統(tǒng)建立對(duì)應(yīng)的底層操作類
    AsynchronousServerSocketChannel channel = AsynchronousServerSocketChannel.open();
    channel.bind(new InetSocketAddress(8082));
    while (true) {
      Future<AsynchronousSocketChannel> future = channel.accept();
      try {
        AsynchronousSocketChannel asc = future.get();
        System.out.println("建立連接成功");
        Future<Integer> write = asc.write(ByteBuffer.wrap("Now let's exchange datas".getBytes(StandardCharsets.UTF_8)));
        while (!write.isDone()) {
          TimeUnit.SECONDS.sleep(2);
        }
        System.out.println("發(fā)送數(shù)據(jù)完成");
        asc.close();
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }

  private static void client() throws Exception {
    AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
    Future<Void> future = socketChannel.connect(new InetSocketAddress(8082));
    while (!future.isDone()) {
      TimeUnit.SECONDS.sleep(2);
    }
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    Future<Integer> read = socketChannel.read(buffer);
    while (!read.isDone()) {
      TimeUnit.SECONDS.sleep(2);
    }
    System.out.println("接收服務(wù)器數(shù)據(jù):" + new String(buffer.array(), 0, read.get()));
  }

以上就是Java BIO,NIO,AIO總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于Java BIO,NIO,AIO的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java匿名內(nèi)部類原理與用法詳解

    Java匿名內(nèi)部類原理與用法詳解

    這篇文章主要介紹了Java匿名內(nèi)部類原理與用法,結(jié)合實(shí)例形式分析了Java匿名內(nèi)部類的概念、原理、應(yīng)用與相關(guān)操作注意事項(xiàng),需要的朋友可以參考下
    2019-09-09
  • 手把手帶你實(shí)現(xiàn)一個(gè)萌芽版的Spring容器

    手把手帶你實(shí)現(xiàn)一個(gè)萌芽版的Spring容器

    大家好,我是老三,Spring是我們最常用的開(kāi)源框架,經(jīng)過(guò)多年發(fā)展,Spring已經(jīng)發(fā)展成枝繁葉茂的大樹(shù),讓我們難以窺其全貌,這節(jié),我們回歸Spring的本質(zhì),五分鐘手?jǐn)]一個(gè)Spring容器,揭開(kāi)Spring神秘的面紗
    2022-03-03
  • java實(shí)現(xiàn)學(xué)生成績(jī)信息管理系統(tǒng)

    java實(shí)現(xiàn)學(xué)生成績(jī)信息管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)學(xué)生成績(jī)信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-07-07
  • Java可變個(gè)數(shù)形參的方法實(shí)例代碼

    Java可變個(gè)數(shù)形參的方法實(shí)例代碼

    這篇文章主要給大家介紹了關(guān)于Java可變個(gè)數(shù)形參的相關(guān)資料,文中通過(guò)圖文以及實(shí)例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-02-02
  • Java的JSON格式轉(zhuǎn)換庫(kù)GSON的初步使用筆記

    Java的JSON格式轉(zhuǎn)換庫(kù)GSON的初步使用筆記

    GSON是Google開(kāi)發(fā)并在在GitHub上開(kāi)源的Java對(duì)象與JSON互轉(zhuǎn)功能類庫(kù),在Android開(kāi)發(fā)者中也大受歡迎,這里我們就來(lái)看一下Java的JSON格式轉(zhuǎn)換庫(kù)GSON的初步使用筆記:
    2016-06-06
  • mybatis-plus動(dòng)態(tài)表名實(shí)現(xiàn)方法

    mybatis-plus動(dòng)態(tài)表名實(shí)現(xiàn)方法

    本文主要介紹了mybatis-plus動(dòng)態(tài)表名實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • Java微信公眾號(hào)安全模式消息解密

    Java微信公眾號(hào)安全模式消息解密

    這篇文章主要為大家詳細(xì)介紹了Java微信公眾號(hào)安全模式消息解密,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-12-12
  • Java?代碼本地設(shè)置Hadoop用戶名密碼的方法

    Java?代碼本地設(shè)置Hadoop用戶名密碼的方法

    在Hadoop環(huán)境中,通常使用Kerberos進(jìn)行身份驗(yàn)證,這篇文章主要介紹了Java?代碼本地設(shè)置Hadoop用戶名密碼的方法,需要的朋友可以參考下
    2024-08-08
  • Java中的StackOverflowError錯(cuò)誤問(wèn)題及解決方法

    Java中的StackOverflowError錯(cuò)誤問(wèn)題及解決方法

    這篇文章主要介紹了Java中的StackOverflowError錯(cuò)誤,在本文中,我們仔細(xì)研究了StackOverflower錯(cuò)誤,包括Java代碼如何導(dǎo)致它,以及我們?nèi)绾卧\斷和修復(fù)它,需要的朋友可以參考下
    2022-07-07
  • Java?windows環(huán)境構(gòu)建圖文教程

    Java?windows環(huán)境構(gòu)建圖文教程

    這篇文章主要為大家介紹了Java?windows環(huán)境構(gòu)建圖文教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>
    2023-12-12

最新評(píng)論