天天看點

JDK7 AIO介紹1. JDK7 AIO初體驗2. 關于AIO的概念了解3. JDK7 AIO初體驗4. AIO和NIO性能哪個好5. 使用J2SE進行伺服器架構技術選型的變遷6. 原文連結

1. JDK7 AIO初體驗

JDK7已經release一段時間了,有個重要的新特性是AIO。今天趁閑暇,簡單體驗了下,簡單分享如下:

2. 關于AIO的概念了解

關于AIO的概念,僅談談個人的一點了解。可能不到位,請大家指出。

Io的兩個重要步驟:發起IO請求,和實際的IO操作。在unix網絡程式設計的定義裡異步和非異步概念的差別就是實際的IO操作是否阻塞。如果不是就是異步,如果是就是同步。

而阻塞和非阻塞的差別在于發起IO請求的時候是否會阻塞,如果會就是阻塞,不會就是非阻塞。

本人了解能力有限,想了個例子來輔助自己了解:

小明想要買一本<深入java虛拟機>的書,以下幾個場景可以來了解這幾種io模式:

  1. 如果小明每天都去書店問售貨員說有沒有這本書,如果沒有就回去繼續等待,等下次再過來文。(阻塞)
  2. 如果小明告訴售貨員想買一本<深入java虛拟機>的書,那麼就在家裡等着做其他事情去了,如果書到了售貨員就通知小明,小明再自己過去取。
  3. 如果小明告售貨員想買一本<深入java虛拟機>的書,然後告訴售貨員到了幫他送到某某地方去,就做其他事情去了。小明就不管了,等書到了,售貨員就幫他送到那個地方了。

    售貨員可以認為是作業系統的一個服務,而小明是一個使用者程序。不知道是否有誤,如果有誤請大家拍磚指出,謝謝。

    可以看出2,3的效率明顯要比1高。但是1最簡單,而2,3需要一些協作。充分證明了團隊合作的力量。

3. JDK7 AIO初體驗

AsynchronousChannel:支援異步通道,包括服務端AsynchronousServerSocketChannel和普通AsynchronousSocketChannel等實作。

CompletionHandler:使用者處理器。定義了一個使用者處理就緒事件的接口,由使用者自己實作,異步io的資料就緒後回調該處理器消費或處理資料。

AsynchronousChannelGroup:一個用于資源共享的異步通道集合。處理IO事件和配置設定給CompletionHandler。(具體這塊還沒細看代碼,後續再分析這塊)

以一個簡單監聽服務端為例,基本過程是:

  1. 啟動一個服務端通道
  2. 定義一個事件處理器,使用者事件完成的時候處理,如消費資料。
  3. 向系統注冊一個感興趣的事件,如接受資料,并把事件完成的處理器傳遞給系統。
  4. 都已經交待完畢,可以隻管繼續做自己的事情了,作業系統在完成事件後通過其他的線程會自動調用處理器完成事件處理

以下用一個例子來簡單實作,一個服務端和用戶端。服務端監聽用戶端的消息,并列印出來

AIOServer.java

package io.aio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 
 * @author noname
 */
public class AIOServer {
    public final static int PORT = ;
    private AsynchronousServerSocketChannel server;

    public AIOServer() throws IOException {
        server = AsynchronousServerSocketChannel.open().bind(
                new InetSocketAddress(PORT));
    }

    public void startWithFuture() throws InterruptedException,
            ExecutionException, TimeoutException {
        System.out.println("Server listen on " + PORT);
        Future<AsynchronousSocketChannel> future = server.accept();
        AsynchronousSocketChannel socket = future.get();
        ByteBuffer readBuf = ByteBuffer.allocate();
        readBuf.clear();
        socket.read(readBuf).get(, TimeUnit.SECONDS);
        readBuf.flip();
        System.out.printf("received message:" + new String(readBuf.array()));
        System.out.println(Thread.currentThread().getName());

    }

    public void startWithCompletionHandler() throws InterruptedException,
            ExecutionException, TimeoutException {
        System.out.println("Server listen on " + PORT);
        //注冊事件和事件完成後的處理器
        server.accept(null,
                new CompletionHandler<AsynchronousSocketChannel, Object>() {
                    final ByteBuffer buffer = ByteBuffer.allocate();

                    public void completed(AsynchronousSocketChannel result,
                            Object attachment) {
                        System.out.println(Thread.currentThread().getName());
                        System.out.println("start");
                        try {
                            buffer.clear();
                            result.read(buffer).get(, TimeUnit.SECONDS);
                            buffer.flip();
                            System.out.println("received message: "
                                    + new String(buffer.array()));
                        } catch (InterruptedException | ExecutionException e) {
                            System.out.println(e.toString());
                        } catch (TimeoutException e) {
                            e.printStackTrace();
                        } finally {

                            try {
                                result.close();
                                server.accept(null, this);
                            } catch (Exception e) {
                                System.out.println(e.toString());
                            }
                        }

                        System.out.println("end");
                    }

                    @Override
                    public void failed(Throwable exc, Object attachment) {
                        System.out.println("failed: " + exc);
                    }
                });
        // 主線程繼續自己的行為
        while (true) {
            System.out.println("main thread");
            Thread.sleep();
        }

    }

    public static void main(String args[]) throws Exception {
        new AIOServer().startWithCompletionHandler();
    }
}
           

AIOClient.java

package io.aio;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;

public class AIOClient {

    public static void main(String... args) throws Exception {
        AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
        client.connect(new InetSocketAddress("localhost", ));
        client.write(ByteBuffer.wrap("test".getBytes())).get();
    }
}
           

服務端寫了兩種處理實作方式,startWithCompletionHandler是通過Handler來處理,startWithFuture是通過Future方式來處理。startWithCompletionHandler方法裡可以看到調用accepte()完成異步注冊後,線程就可以繼續自己的處理了,完全不被這個io所中斷。

從以上來看AIO的代碼簡單了很多,至少比NIO的代碼實作簡單很多。

4. AIO和NIO性能哪個好

Java NIO : 同步非阻塞,伺服器實作模式為一個請求一個線程,即用戶端發送的連接配接請求都會注冊到多路複用器上,多路複用器輪詢到連接配接有I/O請求時才啟動一個線程進行處理。

Java AIO(NIO.2) : 異步非阻塞,伺服器實作模式為一個有效請求一個線程,用戶端的I/O請求都是由OS先完成了再通知伺服器應用去啟動線程進行處理,

NIO方式适用于連接配接數目多且連接配接比較短(輕操作)的架構,比如聊天伺服器,并發局限于應用中,程式設計比較複雜,JDK1.4開始支援。

AIO方式使用于連接配接數目多且連接配接比較長(重操作)的架構,比如相冊伺服器,充分調用OS參與并發操作,程式設計比較複雜,JDK7開始支援

I/O屬于底層操作,需要作業系統支援,并發也需要作業系統的支援,是以性能方面不同作業系統差異會比較明顯。另外NIO的非阻塞,需要一直輪詢,也是一個比較耗資源的。是以出現AIO

5. 使用J2SE進行伺服器架構技術選型的變遷

雖然現在對大多數程式員來講,基本不會再有使用Java開發一個伺服器這樣的任務,但是,這方面的技術研究一下,對自己的技術提高還是非常有幫助的。說不定啥時候能派上用場。

使用Java(J2SE)來設計伺服器産品(不使用開源或其他已有産品)的架構,随着Java的不斷發展,這幾年也發生了很大變化。在JDK1.4之前,使用Java建構伺服器應用本身就很少,是以這裡也就不提了,我們從JDK1.4開始說。

階段1:一個連接配接一個線程

JDK7 AIO介紹1. JDK7 AIO初體驗2. 關于AIO的概念了解3. JDK7 AIO初體驗4. AIO和NIO性能哪個好5. 使用J2SE進行伺服器架構技術選型的變遷6. 原文連結

階段2:伺服器端采用了線程池

JDK7 AIO介紹1. JDK7 AIO初體驗2. 關于AIO的概念了解3. JDK7 AIO初體驗4. AIO和NIO性能哪個好5. 使用J2SE進行伺服器架構技術選型的變遷6. 原文連結

階段1和階段2雖然簡單,但是很實用,在很多場景下仍然是第一選擇。而且程式設計模型業内非常簡單。

階段3:采用非阻塞IO,多路複用技術,又有兩種不同的方式

JDK7 AIO介紹1. JDK7 AIO初體驗2. 關于AIO的概念了解3. JDK7 AIO初體驗4. AIO和NIO性能哪個好5. 使用J2SE進行伺服器架構技術選型的變遷6. 原文連結
JDK7 AIO介紹1. JDK7 AIO初體驗2. 關于AIO的概念了解3. JDK7 AIO初體驗4. AIO和NIO性能哪個好5. 使用J2SE進行伺服器架構技術選型的變遷6. 原文連結

這種方式很重要的一點就是在IO事件發生時得到通知,由程式進行處理。

NIO給程式設計帶來了很大的複雜度,使用NIO開發非常不容易,也很容易犯錯誤,是以,采用别人的架構是一個簡單而自然的選擇,采用grizzly和mina都很不錯,對通用的場景都能滿足要求。這裡提醒一下,不管mina和grizzly,都有一些你不想用的特性,幹擾你想用的功能,需要小心對待,最好自己也能處理mina和grizzly的bug,改進這些架構的功能。

再有,給予NIO來開發SSL也很複雜。

階段4:使用AIO技術

JDK7 AIO介紹1. JDK7 AIO初體驗2. 關于AIO的概念了解3. JDK7 AIO初體驗4. AIO和NIO性能哪個好5. 使用J2SE進行伺服器架構技術選型的變遷6. 原文連結

AIO最大的特性就是事前先設定好事件的回調函數,事件發生時自動調用回調。而且,得到的通知是“IO操作完成”,而不是NIO的“IO操作即将開始”。

使用AIO,在上層開發SSL也也很麻煩。

6. 原文連結

  • http://www.iteye.com/topic/1113611
  • http://windshome.iteye.com/blog/1837558
  • https://zhidao.baidu.com/question/873378431735591372.html