一BIO(同步阻塞)
java的原生io是一種BIO,也就是阻塞io,下面我們将要簡單的模仿一下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等。