Java NIO之理解I/O模型(一)

前言

本身之前在Java NIO這塊兒,一直都是比較薄弱的,之前還由於這點知識而錯失了一個機會。因此最近打算好好學習一下這部份內容,我想應該也會有朋友像我同樣,一直想鬧明白這塊兒內容。可是一直無從下手,每次被問到什麼NIO,BIO,AIO就慌,下面咱們先從一些基本概念來慢慢了解NIO這部份內容。數據庫

同步與異步

同步和異步是比較好理解的,網上也有好多解釋。下面我經過我的的理解來解釋這兩個概念可能會通俗一些,但願能更好理解。bash

同步就是多個任務或事件在執行時須要按順序逐個執行,若是排在順序前面的任務或事件在執行的時候,排在後面的任務或事件就須要等待前面的執行完後才能夠執行,這些任務或事件是不能並行執行的。同步執行任務能夠被設計爲可靠的任務序列,先後兩個任務能夠保持一致纔算整個任務結束。網絡

異步是多個任務或事件能夠同時並行執行,前面的任務不會致使後面的任務的等待。由於是多個任務同時進行的,因此每一個任務之間不產生相互的依賴,因此沒法保證可靠性。多線程

同步流程圖異步

異步流程圖分佈式

同步示例代碼ide

按順序逐個執行的方法,test3會等待test1和test2都執行完後再執行。性能

異步示例代碼學習

public static void testA(){
        new Thread(){
            @Override
            public void run() {
                System.out.println(">>>>>>>>>testA<<<<<<<<<<");
            }
        }.start();
}
public static void testB(){
        new Thread(){
            @Override
            public void run() {
                System.out.println(">>>>>>>>>testB<<<<<<<<<<");
            }
        }.start();
}
public static void testC(){
        new Thread(){
            @Override
            public void run() {
                System.out.println(">>>>>>>>>testC<<<<<<<<<<");
            }
        }.start();
}
public static void main(String[] args) {
        testA();
        testB();
        testC();
}複製代碼

上面這段異步代碼能夠看出,testA、testB、testC三個方法各自有本身的線程來執行任務,五項不依賴因此不會形成有任務等待的狀況。典型的異步處理機制。ui

雖然上面的異步用了三個線程來實現了,可是並不表明多線程就是異步,這是兩個概念,多線程只是實現異步的一種方式。而異步是一種處理模式,除了多線程還能夠有其餘的方式來實現。

在生活中的例子咱們在打電話的時候就至關於同步,只有對方接通了纔算任務執行成功。而發短信則是異步,短信發送後並不依賴接收者是否接收成功。

阻塞與非阻塞

阻塞是指當有任務在執行時,會發出一個請求操做,若是該請求操做須要的條件不知足的話,那麼就會一直等待,直到條件知足後,才繼續執行後面的其餘工做

非阻塞是指當有任務在執行時,會發出一個請求操做,若是該請求操做須要的條件不知足的話,會當即返回一個標誌信息告知條件不知足,而不會一直在等待下去

阻塞流程

非阻塞流程

有的人老是把同步、異步,與阻塞、非阻塞, 這兩組概念給理解混了,可是其實這是兩組徹底不一樣的概念。

同步與異步這組概念的重點在於,前面的任務是否會致使整個流程的等待。

阻塞與非阻塞這組概念的重點在於,若是操做請求不知足條件是否會返回一個標誌信息告知不知足條件。

其實理解阻塞與非阻塞能夠從咱們一般所接觸的線程阻塞來理解,當出現慢任務的時候,線程會發生阻塞,cpu會等待慢任務執行完成後再執行後續的任務。而非阻塞線程在執行這個慢任務的時候,會去作其餘事情,當慢任務執行完成後,再去執行後面的任務。非阻塞雖然看似能夠明顯提升效率,可是系統的線程切換也是會形成時間損耗,因此須要合理利用。

同步IO與異步IO

同步IO是指,當一個線程在執行IO操做時,該線程在IO操做完成前,是會被阻塞的。

異步IO是指,當一個線程在執行IO操做時,該線程並不會被阻塞。

IO操做實際上是有一個過程的,咱們拿網絡IO爲例,一個網絡IO主要會涉及到兩個對象,一個是調用這個IO得線程,另外一個是系統內核。當一個read操做發生時,會經歷兩個階段。

一、等待數據準備就緒。

二、將數據從內核拷貝到調用調用這個IO得線程中。

IO模型的區別主要都在這兩個階段上面因此很重要,咱們所說的同步與異步的區別,在於第二個階段中,將數據從內核拷貝到線程(或進程)中,若是被阻塞了就同步,沒有被阻塞就是異步。被阻塞了說明該階段的操做是依賴用戶線程的,而沒有被阻塞說明不依賴用戶線程,而依賴內核,因此異步是須要操做系統內核支持的。

阻塞IO與非阻塞IO

上面咱們在介紹同步IO與非同步IO的時候說到,同步與不一樣步的區別在IO操做的第二個階段,這節咱們說的阻塞IO與非阻塞IO則是發生在IO操做第一個階段的。

阻塞IO是指當一個線程發起IO操做請求時,系統內核會去查看要操做的數據是否就緒,當是阻塞IO時,發現要操做是數據沒有就緒,就會一直等待下去,直到數據準備就緒;當是非阻塞IO時若是數據沒有準備好,就會返回一個標識信息告訴調用線程,當前操做數據沒有準備就緒。當數據準備就緒後纔會執行第一階段。

其實阻塞IO與非阻塞IO的關鍵區別在於,是等待執行,仍是說當即返回一個通知標識。當數據沒有準備好時就等待執行,而噹噹即返回一個通知標識時,線程會根據標識知道如今數據是個什麼狀況,若是沒有準備好,那麼線程會再次發起請求,知道數據準備好後當即執行。

兩種方式的組合

雖然異步和非阻塞可以提高I/O的性能,可是也會帶來一些額外的性能成本,例如:會增長線程數量,從而增長CPU的消耗,同時也會致使程序設計複雜度的上升。若是設計的不合理反而會致使性能降低,在實際設計時要分解應用場景總和評估。

下面這個表格就列出了同步異步與阻塞非阻塞組合起來的性能分析。

組合方式 性能分析
同步阻塞 最經常使用的一種用法,使用也是最簡單的,可是I/O性能通常不好,CPU大部分處於空閒狀態。
同步非阻塞

  提高I/O性能的經常使用手段,就是將I/O的阻塞改成非阻塞方式,尤爲在網絡I/O是長鏈接同事傳輸

數據也不不少的狀況下,提高性能很是有效。

  這種方式一般能提高I/O性能,可是會增長CPU消耗,要考慮增長的I/O性能能不能補償CPU的

消耗,也就是系統的瓶頸是在I/O上仍是在CPU上。

異步阻塞

  這種方式在分佈式數據庫中常常用到,例如,在一個分佈式數據庫中寫一條記錄,一般會有一份是

同步阻塞的記錄,還有2~3份記錄會寫到其餘機器上,這些備份記錄一般都採用異步阻塞的方式寫I/O。

  異步阻塞對網絡I/O可以提高效率,尤爲像上面這種同時寫多份相同數據的狀況。

異步非阻塞

  這種組合方式用起來比較複雜,只有在一些很是複雜的分佈式狀況下用,集羣之間的消息同步機制

通常用這種I/O組合方式。

  它適合同時要傳多份相同的數據到集羣中不一樣的機器,同時數據的傳輸量雖然不大卻很是頻繁的狀況。

這種網絡I/O用此方式性能達到最高。

文章會同步到個人公衆號上面,歡迎關注。

相關文章
相關標籤/搜索