1. 程式人生 > >NIO程式設計(同步阻塞與同步非阻塞詳解)

NIO程式設計(同步阻塞與同步非阻塞詳解)

NIO同步阻塞與同步非阻塞

BIO與NIO

IO為同步阻塞形式,NIO為同步非阻塞形式,NIO並沒有實現非同步,在JDK1.7後升級NIO庫包,支援非同步非阻塞
同學模型NIO2.0(AIO)

BIO(同步阻塞式IO)

同步阻塞式IO,伺服器實現模式為一個連線一個執行緒,即客戶端有連線請求時伺服器端就需要啟動一個執行緒進行處理,如果這個連線不做任何事情會造成不必要的執行緒開銷,當然可以通過執行緒池機制改善。

NIO(同步非阻塞式IO)

同步非阻塞式IO,伺服器實現模式為一個請求一個執行緒,即客戶端傳送的連線請求都會註冊到多路複用器上,多路複用器輪詢到連線有I/O請求時才啟動一個執行緒進行處理。

AIO(非同步非阻塞式IO)

非同步非阻塞式IO,伺服器實現模式為一個有效請求一個執行緒,客戶端的I/O請求都是由OS先完成了再通知伺服器應用去啟動執行緒進行處理。

BIO(IO)與NIO區別

其本質就是阻塞和非阻塞的區別

什麼是阻塞?

應用程式在獲取網路資料的時候,如果網路傳輸資料很慢,就會一直等待,直到傳輸完畢為止。

什麼是非阻塞?

應用程式直接可以獲取已經準備就緒好的資料,無需等待。

同步時,應用程式會直接參與IO讀寫操作,並且我們的應用程式會直接阻塞到某一個方法上,直到資料準備就緒;或者採用輪訓的策略實時檢查資料的就緒狀態,如果就緒則獲取資料.
非同步時

,則所有的IO讀寫操作交給作業系統,與我們的應用程式沒有直接關係,我們程式不需要關係IO讀寫,當作業系統完成了IO讀寫操作時,會給我們應用程式傳送通知,我們的應用程式直接拿走資料極即可。

偽非同步

問題思考

如何解決同步阻塞IO?
答:使用偽非同步阻塞IO(多執行緒)!

由於BIO一個客戶端需要一個執行緒去處理,因此我們進行優化,後端使用執行緒池來處理多個客戶端的請求接入,形成客戶端個數M:執行緒池最大的執行緒數N的比例關係,其中M可以遠遠大於N,通過執行緒池可以靈活的調配執行緒資源,設定執行緒的最大值,防止由於海量併發接入導致執行緒耗盡。

原理:

當有新的客戶端接入時,將客戶端的Socket封裝成一個Task(該Task任務實現了java的Runnable介面)投遞到後端的執行緒池中進行處理,由於執行緒池可以設定訊息佇列的大小以及執行緒池的最大值,因此,它的資源佔用是可控的,無論多少個客戶端的併發訪問,都不會導致資源的耗盡或宕機。

使用多執行緒支援多個請求

伺服器實現模式為一個連線一個執行緒,即客戶端有連線請求時伺服器端就需要啟動一個執行緒進行處理,如果這個連線不做任何事情會造成不必要的執行緒開銷,當然可以通過執行緒池機制改善。

程式碼示例

Tcp服務端

public class TcpServer {
    public static void main(String[] args) throws IOException {
        System.out.println("Socket tcp服務端啟動。。。。");
        ServerSocket serverSocket = new ServerSocket(8080);
        //等待客戶端請求
        try {
            while (true) {
                Socket accept = serverSocket.accept();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try{
                            InputStream inputStream = accept.getInputStream();
                            //轉換成string型別
                            byte[] bytes = new byte[1024];
                            int read = inputStream.read(bytes);
                            String str = new String(bytes, 0, read);
                            System.out.println("服務端接受客戶端內容:"+str);

                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                }).start();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            serverSocket.close();
        }
    }
}

Tcp客戶端

public class TcpClient {
    public static void main(String[] args) throws IOException {
        System.out.println("Socket tcp客戶端啟動...");
        Socket socket = new Socket("127.0.0.1", 8080);
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("勒布朗-詹姆斯".getBytes());
        socket.close();
    }
}
執行結果

這裡寫圖片描述

伺服器在什麼時候會被等待?

在伺服器端沒有收到客戶端任何資料的時候,一直等待。則被稱為伺服器阻塞IO。

注意⚠️:

但是,偽非同步沒有真正解決阻塞IO核心。
當我們頻繁建立執行緒時,他會佔電腦CPU記憶體,導致電腦宕機,由此,我們在用偽非同步處理同步阻塞IO的時候推薦使用執行緒池來管理!

使用執行緒池管理執行緒

將上面TcpServer改成:

public class TcpServer {
    public static void main(String[] args) throws IOException {
        System.out.println("Socket tcp服務端啟動。。。。");
        ExecutorService executorService = Executors.newCachedThreadPool();
        ServerSocket serverSocket = new ServerSocket(8080);
        //等待客戶端請求
        try {
            while (true) {
                Socket accept = serverSocket.accept();
                executorService.execute(new Runnable() {
                    @Override
                    public void run() {
                        try{
                            InputStream inputStream = accept.getInputStream();
                            //轉換成string型別
                            byte[] bytes = new byte[1024];
                            int read = inputStream.read(bytes);
                            String str = new String(bytes, 0, read);
                            System.out.println("服務端接受客戶端內容:"+str);

                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                });
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            serverSocket.close();
        }
    }
}

TcpClient程式碼不變。

IO模型關係

這裡寫圖片描述

NIO非阻塞程式碼

NIO客戶端

public class NioClient {
    //  nio非同步非阻塞IO
    public static void main(String[] args) throws IOException {
        System.out.println("客戶端已經啟動...");
        //1、建立通道
        SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",8080));
        //2、切換非同步非阻塞
        sChannel.configureBlocking(false);
        //3、指定緩衝區大小
        ByteBuffer allocate = ByteBuffer.allocate(1024);

        Scanner scanner = new Scanner(System.in);
        System.out.println("請輸入內容:");
        while (scanner.hasNext()){
            System.out.println("請輸入內容:");
            String str = scanner.next();
            allocate.put((new Date().toString()+"\n"+str).getBytes());
            //4、切換到讀取模式
            allocate.flip();
            sChannel.write(allocate);
            allocate.clear();

        }
        sChannel.close();
    }
}

NIO伺服器端

public class NioServer {
    public static void main(String[] args) throws IOException {
        System.out.println("服務端已經啟動...");
        //1、建立通道
        ServerSocketChannel sChannel = ServerSocketChannel.open();
        //2、切換非同步非阻塞
        sChannel.configureBlocking(false);
        //3、繫結連線
        sChannel.bind(new InetSocketAddress(8080));
        //4、獲取選擇器
        Selector selector =Selector.open();
        //5、將通道註冊到選擇器,並且"指定監聽接受事件"。
        sChannel.register(selector, SelectionKey.OP_ACCEPT);
        //6、輪訓式 獲取選擇"已經準備就緒"的事件
        while (selector.select()>0){
            // 7.獲取當前選擇器所有註冊的"選擇鍵(已經就緒的監聽事件)"
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while(iterator.hasNext()){
                //8.獲取準備就緒的事件
                SelectionKey sk = iterator.next();
                //9.判斷具體是什麼事件準備就緒
                if(sk.isAcceptable()){
                    // 10.若"接受就緒",獲取客戶端連線
                    SocketChannel socketChannel = sChannel.accept();
                    //11.設定阻塞模式
                    socketChannel.configureBlocking(false);
                    //12.將該通道註冊到伺服器上
                    socketChannel.register(selector, SelectionKey.OP_READ);
                }else if(sk.isReadable()){
                    // 13.獲取當前選擇器"就緒" 狀態的通道
                    SocketChannel socketChannel=(SocketChannel)sk.channel();
                    // 14.讀取資料
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    int len=0;
                    while ((len=socketChannel.read(buffer))>0){
                        buffer.flip();
                        System.out.println(new String(buffer.array(),0,len));
                        buffer.clear();
                    }
                }
            }

            iterator.remove();
        }
        sChannel.close();
    }
}

執行結果
客戶端:
這裡寫圖片描述
服務端:
這裡寫圖片描述

選擇KEY

1、SelectionKey.OP_READ
2、SelectionKey.OP_WRITE
3、SelectionKey.OP_ACCEPT
4、SelectionKey.OP_CONNECT

如果你對不止一種事件感興趣,那麼可以用“位或”操作符將常量連線起來,如下:
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
在SelectionKey類的原始碼中我們可以看到如下的4中屬性,四個變數用來表示四種不同型別的事件:可讀、可寫、可連線、可接受連線

相關推薦

IO阻塞IO阻塞2

範式 屬於 線程 bsp 思想 結束 阻塞io 系統 編程範式 ---恢復內容開始--- 事件驅動編程思想:   一種編程範式 阻塞IO:只發了一次系統調用 觸發方式:     1 水平觸發 select屬於水平觸發     2 邊緣觸發          

IntelliJ IDEA 快捷鍵說明大全中英對照、帶圖示 (轉載)

lac 關閉 計算表達 ror 官網 條件 mark ctrl + c 為什麽 其中的英文說明來自於 idea 的官網資料,中文說明主要來自於自己的領會和理解,英文說明只是作為參考。重要的快捷鍵會附帶圖示,進行詳細的說明。 每一部分會先列出所有的快捷鍵說明表,如果有不清楚的

IntelliJ IDEA 快捷鍵說明大全中英對照、帶圖示

show catch 源碼 error 熱鍵 說明 type 機制 edi IntelliJ IDEA 快捷鍵說明大全(中英對照、帶圖示詳解) 因為覺得網絡上的 idea 快捷鍵不夠詳盡,所以特別編寫了此篇文章,方便大家使用 idea O(∩_∩)

Airflow 使用隨筆內涵 TimeZone 和 Backfill 等的

其實怎麼部署  airflow 又哪些特性,然後功能又是如何全面都可以在 Reference 的文章裡面找到,都不是重點這裡就不贅述了。 這裡重點談一下我在部署完成仔細閱讀文件之後覺得可以總結的一些東西,或者踩到的一些坑。 首選明確 airflow 中最重要的幾個概念: DAG

HDU5726 GCD二分 + ST表 RMQ 倍增演算法

GCD Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 5451    Accepted Submission

Android移動支付支付寶支付2017最新接入

接入流程 一:公司開通支付寶商戶號  二:註冊登入螞蟻金服開放平臺建立應用  三:專案整合支付寶SDK 一:公司開通支付寶商戶號 1 註冊商戶號:https://mobiless.alipay.com/ 可以把連結發給人事,讓她弄一下,需要提交的

HDU - 1995 奇妙的塔 漢諾塔遞迴思想

用1,2,...,n表示n個盤子,稱為1號盤,2號盤,...。號數大盤子就大。經典的漢諾塔問  題經常作為一個遞迴的經典例題存在。可能有人並不知道漢諾塔問題的典故。漢諾塔來源於  印度傳說的一個故事,上帝創造世界時作了三根金剛石柱子,在一根柱子上從下往上按大小 

java儲存機制棧、堆、方法區

一、java的六種儲存地址及解釋 1) 暫存器(register):這是最快的儲存區,因為它位於不同於其他儲存區的地方——處理器內部。但是暫存器的數量極其有限,所以暫存器由編譯器根據需求進行分配。你不

捍衛Cookie——沒有Cookie,我們什麼都沒有了第一方cookie和第三方cookie

開始正文前,先說點兒題外話。Google Analytics最近推出了API介面功能,開發人員可以將Google Analytics的資料和功能整合在自己的應用中。詳細內容請見這裡。   話歸正題。當微軟還沒有推出IE8的時候,老實說,我就不喜歡這個瀏覽器。這不是來自於我

Android移動支付支付寶支付2016最新接入

接入流程 一:公司開通支付寶商戶號 二:註冊登入螞蟻金服開放平臺建立應用 三:專案整合支付寶SDK 一:公司開通支付寶商戶號 二:註冊登入螞蟻金服開放平臺建立應用 1 建立應用 2 應用環境設定 應用公鑰配置 點選支付寶

TCP協議含長連線短連線的

1. TCP連線 當網路通訊時採用TCP協議時,在真正的讀寫操作之前,server與client之間必須建立一個連線,當讀寫操作完成後,雙方不再需要這個連線時它們可以釋放這個連線,連線的建立是需要三次握手的,而釋放則需要4次握手,所以說每個連線的建立都是需要資源消耗和時間消耗的 經典的三次握手示意圖: 經

.NET 雲原生架構師訓練營模組二 基礎鞏固 RabbitMQ Masstransit --學習筆記

# 2.6.7 RabbitMQ -- Masstransit 詳解 - Consumer 消費者 - Producer 生產者 - Request-Response 請求-響應 ## Consumer 消費者 在 MassTransit 中,一個消費者可以消費一種或多種訊息 消費者的型別包括:普通消

NIO程式設計同步阻塞同步阻塞

NIO同步阻塞與同步非阻塞 BIO與NIO IO為同步阻塞形式,NIO為同步非阻塞形式,NIO並沒有實現非同步,在JDK1.7後升級NIO庫包,支援非同步非阻塞 同學模型NIO2.0(AIO) BIO(同步阻塞式IO) 同步阻塞式IO

如何解讀 Java IO、NIO 中的同步阻塞同步阻塞

#原文連結:[如何解讀 Java IO、NIO 中的同步阻塞與同步非阻塞?](https://blog.csdn.net/Howinfun/article/details/108388393) # 一、前言 最近剛讀完一本書:《Netty、Zookeeper、Redis 併發實戰》,個人覺得 Netty

理解阻塞阻塞同步異步

返回 check 發出 幾分鐘 分布 才會 理解 ica 消息 作者:嚴肅鏈接:https://www.zhihu.com/question/19732473/answer/20851256來源:知乎著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請註明出處。 “

怎樣理解阻塞阻塞同步異步的區別?

得到 同步 線程 關註 ron 模型 主動 人物 而是 作者:嚴肅鏈接:https://www.zhihu.com/question/19732473/answer/20851256來源:知乎著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請註明出處。 &ld

同步佇列阻塞佇列和阻塞佇列

在併發程式設計中,很多情況下需要使用執行緒安全的佇列。而實現執行緒安全的佇列有兩種實現方式 1、使用阻塞演算法:使用阻塞演算法的佇列可以用一個鎖(入隊和出隊使用同一把鎖)或兩個鎖(入隊和出隊用不同的鎖)等方式來實現(基於鎖的演算法會帶來一些活躍度失敗的風險。如果執行緒在持有

怎樣理解阻塞阻塞同步非同步的區別?

1.同步與非同步 同步和非同步關注的是訊息通訊機制 (synchronous communication/ asynchronous communication) 所謂同步,就是在發出一個呼叫時,在沒有得到結果之前,該呼叫就不返回。但是一旦呼叫返回,就得到返回

《JAVA併發程式設計實戰》原子變數和阻塞同步機制

引言 即使原子變數沒有用於非阻塞演算法的開發,他們也可以用作一種“更好的”volatile型別變數。原子變數提供了與volatile型別變數相同的記憶體語義,此外還支援原子的更新操作,從而使他們更加適用於實現計數器、序列發生器和統計資料收集等,同時還能比基於鎖

併發程式設計、並行、多執行緒、鎖、同步、非同步、多執行緒、單執行緒、阻塞io、阻塞io

一、 cpu的每一個核在同一時間下,只能執行一個執行緒,就是單核同一時間只能執行一個執行緒 而cpu可以不停的切換,這樣就導致使用者感覺可以執行多個執行緒,這是併發,而不是並行 併發和並行 你吃飯吃到一半,電話來了,你一直到吃完了以後才去接,這就說明你不支援併發也不支