亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

通過(guò)Java帶你了解網(wǎng)絡(luò)IO模型

 更新時(shí)間:2023年05月24日 10:54:21   作者:nssnail  
這篇文章將通過(guò)Java帶大家了解網(wǎng)絡(luò)IO模型,包括BIO,NoBlockingIO,NIO(NewIO),AIO等做了詳細(xì)得介紹,感興趣的小伙伴可以參考閱讀本文

1.BIO

1.1 簡(jiǎn)述

BIO是同步阻塞IO,所有連接都是同步執(zhí)行的,在上一個(gè)連接未處理完的時(shí)候是無(wú)法接收下一個(gè)連接

1.2 代碼示例

在上述代碼中,如果啟動(dòng)一個(gè)客戶(hù)端起連接服務(wù)端時(shí)如果沒(méi)有發(fā)送數(shù)據(jù),那么下一個(gè)連接將永遠(yuǎn)無(wú)法進(jìn)來(lái)

public static void main(String[] args) {
    try {
        // 監(jiān)聽(tīng)端口
        ServerSocket serverSocket = new ServerSocket(8080);
        // 等待客戶(hù)端的連接過(guò)來(lái),如果沒(méi)有連接過(guò)來(lái),就會(huì)阻塞
        while (true) {
            // 阻塞IO中一個(gè)線程只能處理一個(gè)連接
            Socket socket = serverSocket.accept();
            System.out.println("客戶(hù)端建立連接:"+socket.getPort());
            String line = null;
            try {
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                line = bufferedReader.readLine();
                System.out.println("客戶(hù)端的數(shù)據(jù):" + line);
                BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                bufferedWriter.write("ok\n");
                bufferedWriter.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

1.3優(yōu)點(diǎn)和缺點(diǎn)

優(yōu)點(diǎn):

  • 簡(jiǎn)單易用,代碼實(shí)現(xiàn)比較簡(jiǎn)單。

  • 對(duì)于低并發(fā)量的場(chǎng)景,因?yàn)槊總€(gè)連接都有獨(dú)占的線程處理IO操作,因此可以保證每個(gè)連接的IO操作都能夠及時(shí)得到處理。

  • 對(duì)于數(shù)據(jù)量較小的IO操作,同步阻塞IO模型的性能表現(xiàn)較好。

缺點(diǎn):

  • 由于每一個(gè)客戶(hù)端連接都需要開(kāi)啟一個(gè)線程,因此無(wú)法承載高并發(fā)的場(chǎng)景。

  • 線程切換的開(kāi)銷(xiāo)比較大,會(huì)導(dǎo)致系統(tǒng)性能下降。

  • 對(duì)于IO操作較慢的情況下,會(huì)占用大量的線程資源,導(dǎo)致系統(tǒng)負(fù)載過(guò)高。

  • 對(duì)于處理大量連接的服務(wù)器,BIO模型的性能較低,無(wú)法滿(mǎn)足需求。

1.4 思考

問(wèn):既然每個(gè)連接進(jìn)來(lái)都會(huì)阻塞,那么是否可以使用多線程的方式接收處理?

答:當(dāng)然可以,但是這樣如果有1w個(gè)連接那么就要啟動(dòng)1w個(gè)線程去處理嗎,線程是非常寶貴的資源,頻繁使用線程對(duì)系統(tǒng)的開(kāi)銷(xiāo)是非常大的

2. NoBlockingIO

2.1 簡(jiǎn)述

NoBlockingIO是同步非阻塞IO,相對(duì)比阻塞IO,他在接收數(shù)據(jù)的時(shí)候是非阻塞的,會(huì)一直輪詢(xún)去問(wèn)內(nèi)核是否準(zhǔn)備好數(shù)據(jù),直到有數(shù)據(jù)返回

ps: NoBlockingIO并不是真正意義上的NIO

2.2 代碼示例

在下述代碼中,將BIO中的ServerSocket修改為ServerSocketChannel,然后configureBlocking為false則為非阻塞,從而數(shù)據(jù)都是在channel的buffer(緩沖區(qū))中獲取,不理解沒(méi)關(guān)系,就當(dāng)作是設(shè)置非阻塞IO的方式就好

此時(shí)在accept中是非阻塞的,不斷的等待客戶(hù)端進(jìn)來(lái)

注意

  • accept是非阻塞,不斷輪詢(xún),如果為空則跳過(guò),不為空則添加連接
  • 讀數(shù)據(jù)是非阻塞,不斷的輪詢(xún)連接,等待客戶(hù)端寫(xiě)入數(shù)據(jù)
public static List<SocketChannel> channelList = new ArrayList<>();
public static void main(String[] args) {
    try {
        // 相當(dāng)于serverSocket
        // 1.支持非阻塞  2.數(shù)據(jù)總是寫(xiě)入buffer,讀取也是從buffer中去讀  3.可以同時(shí)讀寫(xiě)
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 設(shè)置非阻塞
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.socket().bind(new InetSocketAddress(8080));
        while (true){
            // 這里將不再阻塞
            SocketChannel socketChannel = serverSocketChannel.accept();
            if(socketChannel != null){
                socketChannel.configureBlocking(false);
                channelList.add(socketChannel);
            }else {
                System.out.println("沒(méi)有請(qǐng)求過(guò)來(lái)?。?!");
            }
            for (SocketChannel client : channelList){
                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                // 也不阻塞
                int num = client.read(byteBuffer);
                if(num>0){
                    System.out.println("客戶(hù)端端口:"+ client.socket().getPort()+",客戶(hù)端收據(jù):"+new String(byteBuffer.array()));
                }else {
                    System.out.println("等待客戶(hù)端寫(xiě)數(shù)據(jù)");
                }
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

2.3 優(yōu)點(diǎn)和缺點(diǎn)

優(yōu)點(diǎn):

  • 非阻塞I/O可以同時(shí)處理多個(gè)客戶(hù)端連接,提高服務(wù)器的并發(fā)處理能力。

  • 由于非阻塞I/O的模式下,一個(gè)線程可以處理多個(gè)I/O操作,因此可以減少線程切換次數(shù),提高系統(tǒng)性能

缺點(diǎn):

  • 有很多無(wú)效訪問(wèn),因?yàn)闆](méi)有連接的時(shí)候accept也不會(huì)阻塞,很多為空的accpet
  • 如果客戶(hù)端沒(méi)有寫(xiě)數(shù)據(jù),會(huì)一直向內(nèi)核訪問(wèn),每次都是一個(gè)系統(tǒng)調(diào)用,非常浪費(fèi)系統(tǒng)資源

2.4 思考

問(wèn) :既然一直輪詢(xún)會(huì)產(chǎn)生很多的無(wú)效輪詢(xún),并浪費(fèi)系統(tǒng)資源,那么有沒(méi)有更好的辦法呢

答: 通過(guò)事件注冊(cè)的方式(多路復(fù)用器)

3. NIO(NewIO)

3.1 簡(jiǎn)述

NewIO才是真正意義上的NIO,NoBlockingIO只能算是NIO的前身,因?yàn)镹ewIO在NoBlockingIO上加上了多路復(fù)用器,使得NIO更加完美

在下圖中,channel不再是直接循環(huán)調(diào)用內(nèi)核,而是將連接,接收,讀取,寫(xiě)入等事件注冊(cè)到多路復(fù)用器中,如果沒(méi)有事件到來(lái)將會(huì)阻塞等待

NIO三件套(記):

  • channel: 介于字節(jié)緩沖區(qū)(buffer)和套接字(socket)之間,可以同時(shí)讀寫(xiě),支持異步IO
  • buffer: 字節(jié)緩沖區(qū),是應(yīng)用程序和通道之間進(jìn)行IO數(shù)據(jù)傳輸?shù)闹修D(zhuǎn)
  • selector:多路復(fù)用器,監(jiān)聽(tīng)服務(wù)端和客戶(hù)端的管道上注冊(cè)的事件

3.2 代碼示例

從代碼示例可以看到,在沒(méi)有連接的時(shí)候會(huì)在selector.select()中阻塞,然后等待客戶(hù)端連接或者寫(xiě)入數(shù)據(jù),不同的監(jiān)聽(tīng)事件會(huì)有不同的處理方法

具體流程:

  • 服務(wù)端創(chuàng)建Selector,并注冊(cè)O(shè)P_ACCEPT接受連接事件,然后調(diào)用select阻塞等待連接進(jìn)來(lái)

  • 客戶(hù)端注冊(cè)O(shè)P_CONNECT事件,表示連接客戶(hù)端,連接成功后會(huì)調(diào)用handlerConnect方法

    2.1 handlerConnect方法會(huì)注冊(cè)O(shè)P_READ事件并向服務(wù)端寫(xiě)數(shù)據(jù)

  • 這時(shí)候服務(wù)端會(huì)收到OP_ACCEPT后就會(huì)走到handlerAccept方法,表示接受連接

  • 3.1handlerAccept方法也會(huì)注冊(cè)一個(gè)OP_READ事件并向客戶(hù)端寫(xiě)數(shù)據(jù)

  • 客戶(hù)端接收到服務(wù)端的數(shù)據(jù)后會(huì)再次喚醒select方法,然后判斷為isReadable(讀事件,服務(wù)端寫(xiě)入給客戶(hù)端,那么客戶(hù)端就是讀),handlerRead方法將會(huì)把服務(wù)端寫(xiě)入的數(shù)據(jù)讀取

  • 反之亦然,服務(wù)端也會(huì)收到客戶(hù)端寫(xiě)入的數(shù)據(jù),然后通過(guò)讀事件將數(shù)據(jù)讀取

服務(wù)端代碼

public class NewIOServer {
    static Selector selector;
    public static void main(String[] args) {
        try {
            selector = Selector.open();
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.socket().bind(new InetSocketAddress(8080));
            // 需要把serverSocketChannel注冊(cè)到多路復(fù)用器上
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            while (true) {
                // 阻塞
                selector.select();
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    iterator.remove();
                    if (key.isAcceptable()) {
                        handlerAccept(key);
                    } else if (key.isReadable()) {
                        handlerRead(key);
                    }else if(key.isWritable()){
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private static void handlerRead(SelectionKey key) {
        SocketChannel socketChannel = (SocketChannel) key.channel();
        ByteBuffer allocate = ByteBuffer.allocate(1024);
        try {
            socketChannel.read(allocate);
            System.out.println("server msg:" + new String(allocate.array()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private static void handlerAccept(SelectionKey key) {
        ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
        // 不阻塞
        try {
            SocketChannel socketChannel = serverSocketChannel.accept();
            socketChannel.configureBlocking(false);
            socketChannel.write(ByteBuffer.wrap("It‘s server msg".getBytes()));
            // 讀取客戶(hù)端的數(shù)據(jù)
            socketChannel.register(selector, SelectionKey.OP_READ);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

客戶(hù)端代碼

public class NewIOClient {
    static Selector selector;
    public static void main(String[] args) {
        try {
            selector = Selector.open();
            SocketChannel socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(false);
            socketChannel.connect(new InetSocketAddress("localhost", 8080));
            // 需要把socketChannel注冊(cè)到多路復(fù)用器上
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
            while (true) {
                // 阻塞
                selector.select();
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    iterator.remove();
                    if (key.isConnectable()) {
                        handlerConnect(key);
                    } else if (key.isReadable()) {
                        handlerRead(key);
                    } else if (key.isWritable()) {
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private static void handlerRead(SelectionKey key) {
        SocketChannel socketChannel = (SocketChannel) key.channel();
        ByteBuffer allocate = ByteBuffer.allocate(1024);
        try {
            socketChannel.read(allocate);
            System.out.println("client msg:" + new String(allocate.array()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private static void handlerConnect(SelectionKey key) throws IOException {
        SocketChannel socketChannel = (SocketChannel) key.channel();
        if (socketChannel.isConnectionPending()) {
            socketChannel.finishConnect();
        }
        socketChannel.configureBlocking(false);
        socketChannel.write(ByteBuffer.wrap("it‘s client msg".getBytes()));
        socketChannel.register(selector,SelectionKey.OP_READ);
    }
}

3.3 優(yōu)點(diǎn)和缺點(diǎn)

優(yōu)點(diǎn):

  • NIO使用了非阻塞IO,可以大大提高系統(tǒng)的吞吐量和并發(fā)性能。

  • NIO提供了可擴(kuò)展的選擇器,可以監(jiān)控多個(gè)通道的狀態(tài),從而實(shí)現(xiàn)高效的事件驅(qū)動(dòng)模型。

  • NIO采用直接內(nèi)存緩沖區(qū),可以避免Java堆內(nèi)存的GC問(wèn)題,提高內(nèi)存管理的效率。

缺點(diǎn):

  • NIO的編程模型相比傳統(tǒng)的IO模型更加復(fù)雜,需要掌握較多的API和概念。
  • NIO的實(shí)現(xiàn)難度較高,需要處理很多細(xì)節(jié)問(wèn)題,如緩沖區(qū)的管理、選擇器的使用等。
  • NIO的可靠性不如傳統(tǒng)的IO模型,容易出現(xiàn)空輪詢(xún)、系統(tǒng)負(fù)載過(guò)高等問(wèn)題。

3.4 思考

問(wèn):select方法不是也阻塞嗎,那跟BIO有什么區(qū)別?

答:雖然他是在select阻塞,但是他通過(guò)事件注冊(cè)的方式,可以將多個(gè)selectKey同時(shí)加載到selectionKeys集合中,通過(guò)for循環(huán)處理不同的事件,而B(niǎo)IO只能由一個(gè)連接處理完才能處理下一個(gè)連接

問(wèn):什么是多路復(fù)用?

答:

多路:是指多個(gè)連接的管道,通道

復(fù)用:復(fù)用一個(gè)系統(tǒng)調(diào)用,原本多次系統(tǒng)調(diào)用變成一次

4. 擴(kuò)展select/poll、epoll

4.1 簡(jiǎn)述

由第三部分的NIO可知,多路復(fù)用把for循環(huán)的系統(tǒng)調(diào)用變成了一次調(diào)用,那么他具體是怎么實(shí)現(xiàn)的?

其實(shí)我們仔細(xì)思考一下就能知道,他主要實(shí)現(xiàn)就是在selector.select(),由他去阻塞和觸發(fā)動(dòng)作。然而在實(shí)現(xiàn)這些功能的時(shí)候,就用到了三種模型,select、poll、epoll。因?yàn)閟elect和poll很相似,所以大家都會(huì)把他們歸為一類(lèi)。

4.2 select/poll

我們先來(lái)說(shuō)說(shuō)什么是select?

實(shí)現(xiàn)過(guò)程

  • 每一個(gè)socket調(diào)用select()方法后,socket的等待隊(duì)列就會(huì)放線程的引用,該線程就是你調(diào)用select的那個(gè)線程
  • 當(dāng)其中一個(gè)socket發(fā)送數(shù)據(jù)的時(shí)候,他會(huì)將每一個(gè)socket在等待隊(duì)列中移除放入就緒隊(duì)列,這就表明一定有一個(gè)客戶(hù)端寫(xiě)了數(shù)據(jù)過(guò)來(lái),但是注意,這并不表示所有都有客戶(hù)端寫(xiě)了數(shù)據(jù)過(guò)來(lái)
  • 這時(shí)候喚醒主線程,然后去就緒隊(duì)列中遍歷找到客戶(hù)端寫(xiě)的數(shù)據(jù)并返回

具體如下圖所示:

產(chǎn)生問(wèn)題

  • 因?yàn)閒d(file)是個(gè)數(shù)組,所以socket容量會(huì)有上限
  • 只要有一個(gè)socket寫(xiě)入就會(huì)遍歷所有socket,雖然減少了空輪詢(xún)問(wèn)題,但是每次都要在所有socket中去找到已準(zhǔn)備好的那個(gè)socket需要消耗性能

什么是poll?

因?yàn)閒d是個(gè)數(shù)組,所以容量會(huì)達(dá)到上限,而poll則將這個(gè)數(shù)據(jù)結(jié)構(gòu)改成了鏈表,所以解決了select模型中上限的問(wèn)題,但是遍歷socket的問(wèn)題還是存在

select和poll的本質(zhì)區(qū)別就是一個(gè)是用數(shù)組存放socket,一個(gè)是用鏈表存放,其他地方?jīng)]有任何區(qū)別

4.3 epoll

epoll和select/poll相比,采用了事件回調(diào)的機(jī)制,并且使用紅黑樹(shù)去維護(hù)注冊(cè)的socket,如下圖所示

實(shí)現(xiàn)過(guò)程

  • 調(diào)用Selector.open的時(shí)候會(huì)創(chuàng)建一個(gè)eventpoll的文件,里面主要含有等待隊(duì)列,rbr(紅黑樹(shù)),就緒列表
  • 然后在建立連接的時(shí)候調(diào)用epoll_ctl函數(shù)將socket放入epitem中
  • 調(diào)用epoll_wait函數(shù)將線程放入等待隊(duì)列中,等待數(shù)據(jù)過(guò)來(lái)時(shí)喚醒
  • 有數(shù)據(jù)寫(xiě)入的時(shí)候會(huì)觸發(fā)epitem的回調(diào)方法,將該epitem移除并加入rdlist就緒列表中
  • 當(dāng)有數(shù)據(jù)在就緒列表的時(shí)候,就會(huì)喚醒等待對(duì)列中的線程并處理數(shù)據(jù)

這樣通過(guò)紅黑樹(shù)來(lái)維護(hù)連接和通過(guò)就緒列表來(lái)處理數(shù)據(jù)就可以保證可以存放最大限度的socket數(shù)量,并且在喚醒線程處理去處理就緒列表的時(shí)候肯定都是需要處理并且已就緒的socket。完美的解決了select/poll中的問(wèn)題

總結(jié)

epoll相較于select/poll的優(yōu)勢(shì):

  • 采用了事件驅(qū)動(dòng)的方式,可以處理大量的連接,效率更高。

  • 支持邊緣觸發(fā)(ET)和水平觸發(fā)(LT)兩種模式,可以更靈活地處理IO事件。

  • 記錄了上次處理的位置,可以避免重復(fù)的遍歷,更加高效。

  • 高效利用了內(nèi)核空間和用戶(hù)空間的交互,避免了復(fù)制文件描述符。

4.4 擴(kuò)展話題

對(duì)于epoll的一些擴(kuò)展,有興趣的可以了解下,不感興趣可以略過(guò)

4.4.1 什么是ET和LT?

ET和LT是epoll工作模式中的兩種觸發(fā)方式,分別表示邊緣觸發(fā)(Edge Triggered)和水平觸發(fā)(Level Triggered)。

  • 邊緣觸發(fā)(ET)

在ET模式下,當(dāng)一個(gè)文件描述符上出現(xiàn)事件時(shí),epoll_wait函數(shù)只會(huì)通知一次,即只有在文件描述符狀態(tài)發(fā)生變化時(shí)才會(huì)返回。如果應(yīng)用程序沒(méi)有處理完這個(gè)事件,那么下一次調(diào)用epoll_wait函數(shù)時(shí),它不會(huì)再返回這個(gè)事件,直到下一次狀態(tài)變化。

ET模式下的事件處理更為高效,因?yàn)樗粫?huì)在必要的時(shí)候通知應(yīng)用程序,避免了重復(fù)通知的問(wèn)題。但是,由于ET模式只在狀態(tài)變化時(shí)通知一次,因此應(yīng)用程序需要及時(shí)處理事件,否則可能會(huì)錯(cuò)過(guò)某些事件。

  • 水平觸發(fā)(LT)

在LT模式下,當(dāng)一個(gè)文件描述符上出現(xiàn)事件時(shí),epoll_wait函數(shù)會(huì)重復(fù)通知應(yīng)用程序,直到該文件描述符上的事件被處理完畢為止。如果應(yīng)用程序沒(méi)有處理完這個(gè)事件,那么下一次調(diào)用epoll_wait函數(shù)時(shí),它會(huì)再次返回這個(gè)事件,直到應(yīng)用程序處理完為止。

LT模式下的事件處理比較簡(jiǎn)單,因?yàn)樗鼤?huì)重復(fù)通知應(yīng)用程序,直到應(yīng)用程序處理完為止。但是,由于重復(fù)通知的問(wèn)題,LT模式下可能會(huì)導(dǎo)致一些性能問(wèn)題。同時(shí),在LT模式下,應(yīng)用程序需要及時(shí)處理事件,否則可能會(huì)導(dǎo)致文件描述符上的事件積壓,影響系統(tǒng)的性能。

4.4.2 什么是驚群?

epoll的驚群(Thundering Herd)指的是多個(gè)線程或進(jìn)程同時(shí)等待同一個(gè)epoll文件描述符上的事件,

當(dāng)文件描述符上出現(xiàn)事件時(shí),內(nèi)核會(huì)通知所有等待的線程或進(jìn)程,但只有一個(gè)線程或進(jìn)程能夠真正處理該事件,其他線程或進(jìn)程會(huì)被喚醒但不能處理該事件,從而造成資源浪費(fèi)和性能降低的問(wèn)題。

驚群?jiǎn)栴}是由于內(nèi)核通知等待線程或進(jìn)程的方式引起的。在epoll中,當(dāng)文件描述符上出現(xiàn)事件時(shí),內(nèi)核會(huì)通知所有等待的線程或進(jìn)程,而不是通知一個(gè)線程或進(jìn)程。因此,如果有多個(gè)線程或進(jìn)程等待同一個(gè)文件描述符,那么當(dāng)該文件描述符上出現(xiàn)事件時(shí),內(nèi)核會(huì)通知所有等待的線程或進(jìn)程,導(dǎo)致驚群?jiǎn)栴}。

為了解決驚群?jiǎn)栴},可以采用以下兩種方式:

  • 使用邊緣觸發(fā)(ET)模式:在ET模式下,當(dāng)文件描述符上出現(xiàn)事件時(shí),內(nèi)核只會(huì)通知一個(gè)等待的線程或進(jìn)程,從而避免了驚群?jiǎn)栴}。
  • 采用互斥量或條件變量等機(jī)制:在多個(gè)線程或進(jìn)程等待同一個(gè)文件描述符時(shí),可以使用互斥量或條件變量等機(jī)制來(lái)控制線程或進(jìn)程的喚醒,從而避免驚群?jiǎn)栴}。

5. AIO

5.1簡(jiǎn)述

在上面將的BIO,NIO中都是同步IO,BIO叫做同步阻塞,NIO叫做同步非阻塞,那么AIO則是異步IO,全名(Asynchronous I/O)

5.2 代碼示例

從代碼示例可以看到,以下代碼都是基于回調(diào)機(jī)制實(shí)現(xiàn)的,并不會(huì)像BIO和NIO一樣使用輪詢(xún)的方式,他不需要像同步IO一樣需要查找就緒socket,只要客戶(hù)端有數(shù)據(jù)寫(xiě)入就會(huì)回調(diào)給服務(wù)端,既然是異步的所以就不會(huì)存在阻塞

服務(wù)端代碼

public class AIOServer {
    public static void main(String[] args) throws Exception {
        // 創(chuàng)建一個(gè)SocketChannel并綁定了8080端口
        final AsynchronousServerSocketChannel serverChannel =
                AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(8080));
        serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
            @Override
            public void completed(AsynchronousSocketChannel socketChannel, Object attachment) {
                try {
                    // 打印線程的名字
                    System.out.println("2--"+Thread.currentThread().getName());
                    System.out.println(socketChannel.getRemoteAddress());
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    // socketChannel異步的讀取數(shù)據(jù)到buffer中
                    socketChannel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
                        @Override
                        public void completed(Integer result, ByteBuffer buffer) {
                            // 打印線程的名字
                            System.out.println("3--"+Thread.currentThread().getName());
                            buffer.flip();
                            System.out.println(new String(buffer.array(), 0, result));
                            socketChannel.write(ByteBuffer.wrap("HelloClient".getBytes()));
                        }
                        @Override
                        public void failed(Throwable exc, ByteBuffer buffer) {
                            exc.printStackTrace();
                        }
                    });
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            @Override
            public void failed(Throwable exc, Object attachment) {
                exc.printStackTrace();
            }
        });
        System.out.println("1--"+Thread.currentThread().getName());
        Thread.sleep(Integer.MAX_VALUE);
    }
}

客戶(hù)端代碼

public class AIOClient {
    private final AsynchronousSocketChannel client;
    public AIOClient() throws IOException {
        client = AsynchronousSocketChannel.open();
    }
    public static void main(String[] args) throws Exception {
        new AIOClient().connect("localhost",8080);
    }
    public void connect(String host, int port) throws Exception {
        // 客戶(hù)端向服務(wù)端發(fā)起連接
        client.connect(new InetSocketAddress(host, port), null, new CompletionHandler<Void, Object>() {
            @Override
            public void completed(Void result, Object attachment) {
                try {
                    client.write(ByteBuffer.wrap("這是一條測(cè)試數(shù)據(jù)".getBytes())).get();
                    System.out.println("已發(fā)送到服務(wù)端");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            @Override
            public void failed(Throwable exc, Object attachment) {
                exc.printStackTrace();
            }
        });
        final ByteBuffer bb = ByteBuffer.allocate(1024);
        // 客戶(hù)端接收服務(wù)端的數(shù)據(jù),獲取的數(shù)據(jù)寫(xiě)入到bb中
        client.read(bb, null, new CompletionHandler<Integer, Object>() {
            @Override
            public void completed(Integer result, Object attachment) {
                // 服務(wù)端返回?cái)?shù)據(jù)的長(zhǎng)度result
                System.out.println("I/O操作完成:" + result);
                System.out.println("獲取反饋結(jié)果:" + new String(bb.array()));
            }
            @Override
            public void failed(Throwable exc, Object attachment) {
                exc.printStackTrace();
            }
        });
        try {
            Thread.sleep(Integer.MAX_VALUE);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

5.3 優(yōu)點(diǎn)和缺點(diǎn)

優(yōu)勢(shì):

  • 更加高效:AIO采用回調(diào)方式,可以避免輪詢(xún)等操作對(duì)CPU的占用,減少CPU的負(fù)擔(dān),從而提高了系統(tǒng)的性能。

  • 可以更好地利用系統(tǒng)資源:AIO能夠在I/O操作完成之前把線程釋放出來(lái),可以更好地利用系統(tǒng)資源,提高系統(tǒng)的并發(fā)處理能力。

  • 適用于高并發(fā)場(chǎng)景:AIO適用于高并發(fā)場(chǎng)景,能夠支持大量的并發(fā)連接,提高系統(tǒng)的處理能力。

缺點(diǎn):

  • 學(xué)習(xí)成本高:相比于NIO,AIO的編程模型更加復(fù)雜,需要學(xué)習(xí)更多的知識(shí),學(xué)習(xí)成本更高。

  • 實(shí)現(xiàn)難度大:AIO的實(shí)現(xiàn)難度比較大,需要對(duì)操作系統(tǒng)的底層機(jī)制有深入的了解,因此開(kāi)發(fā)成本較高。

  • 并非所有操作系統(tǒng)都支持:AIO并非所有操作系統(tǒng)都支持,只有Linux 2.6以上的內(nèi)核才支持AIO,因此跨平臺(tái)的支持較差。

ps: 說(shuō)白了AIO很好用,但是太復(fù)雜

以上就是通過(guò)Java帶你了解網(wǎng)絡(luò)IO模型的詳細(xì)內(nèi)容,更多關(guān)于Java 網(wǎng)絡(luò)IO模型的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • IDEA打包普通web項(xiàng)目操作

    IDEA打包普通web項(xiàng)目操作

    這篇文章主要介紹了IDEA打包普通web項(xiàng)目操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-09-09
  • 關(guān)于TreeMap自定義排序規(guī)則的兩種方式

    關(guān)于TreeMap自定義排序規(guī)則的兩種方式

    這篇文章主要介紹了關(guān)于TreeMap自定義排序規(guī)則的兩種方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • httpclient提交json參數(shù)的示例詳解

    httpclient提交json參數(shù)的示例詳解

    httpclient使用post提交json參數(shù),和使用表單提交區(qū)分,本文結(jié)合示例代碼講解的非常詳細(xì),補(bǔ)充介紹了HttpClient請(qǐng)求傳json參數(shù)的案例代碼,感興趣的朋友一起看看吧
    2024-02-02
  • java基礎(chǔ)二叉搜索樹(shù)圖文詳解

    java基礎(chǔ)二叉搜索樹(shù)圖文詳解

    二叉樹(shù)是一種非常重要的數(shù)據(jù)結(jié)構(gòu),它同時(shí)具有數(shù)組和鏈表各自的特點(diǎn),下面這篇文章主要給大家介紹了關(guān)于java基礎(chǔ)二叉搜索樹(shù)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-03-03
  • 帶你了解Java數(shù)據(jù)結(jié)構(gòu)和算法之無(wú)權(quán)無(wú)向圖

    帶你了解Java數(shù)據(jù)結(jié)構(gòu)和算法之無(wú)權(quán)無(wú)向圖

    這篇文章主要為大家介紹了Java數(shù)據(jù)結(jié)構(gòu)和算法之無(wú)權(quán)無(wú)向圖?,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-01-01
  • Java中關(guān)于String StringBuffer StringBuilder特性深度解析

    Java中關(guān)于String StringBuffer StringBuilder特性深度解析

    這篇文章主要介紹了Java中關(guān)于String StringBuffer StringBuilder特性深度解析,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-09-09
  • java合成模式之神奇的樹(shù)結(jié)構(gòu)

    java合成模式之神奇的樹(shù)結(jié)構(gòu)

    這篇文章主要介紹了java合成模式,文中運(yùn)用大量的代碼進(jìn)行詳細(xì)講解,希望大家看完本文后能學(xué)習(xí)到相關(guān)的知識(shí),需要的朋友可以參考一下
    2021-08-08
  • mybatis-puls中的resultMap數(shù)據(jù)映射

    mybatis-puls中的resultMap數(shù)據(jù)映射

    這篇文章主要介紹了mybatis-puls中的resultMap數(shù)據(jù)映射,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Serializable接口的作用_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Serializable接口的作用_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    這篇文章主要為大家詳細(xì)介紹了java中Serializable接口的作用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • SpringBoot項(xiàng)目?jī)?yōu)雅的全局異常處理方式(全網(wǎng)最新)

    SpringBoot項(xiàng)目?jī)?yōu)雅的全局異常處理方式(全網(wǎng)最新)

    這篇文章主要介紹了SpringBoot項(xiàng)目?jī)?yōu)雅的全局異常處理方式(全網(wǎng)最新),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04

最新評(píng)論