天天看點

從BIO開始,講到NIO,AIO(1)一BIO(同步阻塞)二NIO(同步不阻塞)

一BIO(同步阻塞)

java的原生io是一種BIO,也就是阻塞io,下面我們将要簡單的模仿一下BIO。

BIO

public class BIO {

    public static void main(String[] args) throws Exception{

        ServerSocket server = new ServerSocket(1999);

        while(true) {

            Socket client = server.accept();

            Thread thread = new Thread(new Runnable() {

                @Override

                public void run() {

                    handler(client);

                }

            });

            thread.start();

        }

    }

    public static void handler(Socket client){

        InputStream input = null;

        try{

            input = client.getInputStream();

            byte[] bytes = new byte[1024];

            input.read(bytes);

            System.out.println(new String(bytes));

            input.close();

        }catch(Exception e) {

            //處理異常

        }finally {

            try {

                input.close();

            } catch (IOException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

        }

    }

}

當然,我們隻是簡單模仿一下BIO,

這裡我們清除,BIO是通過用戶端來一個請求,伺服器端就創造一個線程來處理請求,這樣就是請求和線程是一對一關系,

但是要考慮兩個問題,一個線程開銷,另一個是阻塞問題。

1.線程開銷問題

多線程程式設計并不是開啟線程越多越好,線程切換是需要考慮線程上下文切換的消耗,當切換的消耗太大時,就證明線程太多了。

來一個請求就開一個線程,這就會造成問題,當請求很多,一個是伺服器線程上下文切換造成cpu資源浪費,第二個是會超出伺服器服務能力。

2阻塞問題

在上面這段模仿的BIO代碼上有兩處是阻塞的,一個 server.accept();,另一個是read方法,這就會造成目前線程阻塞,浪費cpu資源。

1.對BIO改進的方案,BIO開啟線程數時根據請求來控制的,可以使用線程池技術來優化,但是效果是不太好的,

2.第二個方案是還是線上程數量上做文章,BIO的線程數時不可控的,但是我們想要把他變成伺服器可以控制的,

3.第三個方法是在阻塞的問題上,主要是由于線程阻塞,不能幹其他事,這主要是對檔案等一些讀寫操作造成的,那我們能不能利用這樣的阻塞時間。

二NIO(同步不阻塞)

個人覺得,NIO是針對改進方案的2,3條來改進,

NIO的工作模式,selector是一個輪詢器,當輪詢到的channel有需要處理的事件,就處理,處理完了接着輪詢。也就會一個selector處理多個channel,這裡的channel可以看做是一個socket,也就是一個請求。channel資料是buffer,buffer是一個塊,通俗來講就是數組,可讀寫。普遍量一個線程隻有一個selector,這樣我們可以控制開多少個線程,一個selector可以處理多個channel,而且是不阻塞的,這樣就可以利用到BIO中線程阻塞的時間。

NIO的三大元件Selector,Channel,Buffer

首先我們來簡單了解一下Buffer,buffer有四個重要變量,一個是mark 是一個變量,初始值為-1,一個是position 這是是接下來需要讀寫的位置,一個capacity,這個是buffer的容量,buffer底層其實就是一個數組,capacity就是數組的大小,最後一個是limit,這個位置是可以讀或者寫到最末位置。讀寫的切換必須調用flip方法。

除boolean 外,其他java的基本資料類型都有對應的buffer,比如ByteBuffer,IntBuffer等。