Java知識回顧 (9) 同步、異步IO

 

1、基本概念

同步和異步:java

同步和異步是針對應用程序和內核的交互而言的。編程

同步指的是用戶進程觸發IO 操做並等待或者輪詢的去查看IO 操做是否就緒;後端

而異步是指用戶進程觸發IO 操做之後便開始作本身的事情,而當IO 操做已經完成的時候會獲得IO 完成的通知。服務器

以銀行取款爲例:網絡

同步 : 本身親自出馬持銀行卡到銀行取錢(使用同步 IO 時,Java 本身處理IO 讀寫);架構

異步 : 委託一小弟拿銀行卡到銀行取錢,而後給你(使用異步IO 時,Java 將 IO 讀寫委託給OS 處理,須要將數據緩衝區地址和大小傳給OS(銀行卡和密碼),OS 須要支持異步IO操做API);併發

 

 

阻塞和非阻塞:阻塞和非阻塞是針對於進程在訪問數據的時候,根據IO操做的就緒狀態來採起的不一樣方式,說白了是一種讀取或者寫入操做方法的實現方式。異步

阻塞方式下,讀取或者寫入函數將一直等待socket

而非阻塞方式下,讀取或者寫入方法會當即返回一個狀態值。ide

以銀行取款爲例:

阻塞 : ATM排隊取款,你只能等待(使用阻塞IO時,Java調用會一直阻塞到讀寫完成才返回);

非阻塞 : 櫃檯取款,取個號,而後坐在椅子上作其它事,等號廣播會通知你辦理,沒到號你就不能去,你能夠不斷問大堂經理排到了沒有,大堂經理若是說還沒到你就不能去(使用非阻塞IO時,若是不能讀寫Java調用會立刻返回,當IO事件分發器通知可讀寫時再繼續進行讀寫,不斷循環直到讀寫完成)

 

2、應用方式

2.1 BIO 編程

Blocking IO: 同步阻塞的編程方式。

BIO編程方式一般是在JDK1.4版本以前經常使用的編程方式。編程實現過程爲:

首先,在服務端啓動一個ServerSocket來監聽網絡請求,客戶端啓動Socket發起網絡請求。

默認狀況下ServerSocket回創建一個線程來處理此請求,若是服務端沒有線程可用,客戶端則會阻塞等待或遭到拒絕。

且創建好的鏈接,在通信過程當中,是同步的。在併發處理效率上比較低。大體結構以下:

同步並阻塞,服務器實現模式爲一個鏈接一個線程,即客戶端有鏈接請求時服務器端就須要啓動一個線程進行處理,若是這個鏈接不作任何事情會形成沒必要要的線程開銷,固然能夠經過線程池機制改善。

BIO方式適用於鏈接數目比較小且固定的架構,這種方式對服務器資源要求比較高,併發侷限於應用中,JDK1.4之前的惟一選擇,但程序直觀簡單易理解。

使用線程池機制改善後的BIO模型圖以下:

 

2.NIO 編程

Unblocking IO(New IO): 同步非阻塞的編程方式。

NIO自己是基於事件驅動思想來完成的,其主要想解決的是BIO的大併發問題。NIO基於Reactor,當socket有流可讀或可寫入socket時,操做系統會相應的通知引用程序進行處理應用再將流讀取到緩衝區或寫入操做系統

也就是說,這個時候,已經不是一個鏈接就要對應一個處理線程了,而是有效的請求,對應一個線程,當鏈接沒有數據時,是沒有工做線程來處理的。

 

NIO的最重要的地方是,當一個鏈接建立後,不須要對應一個線程,這個鏈接會被註冊到多路複用器上面。因此,全部的鏈接只須要一個線程就能夠搞定,當這個線程中的多路複用器進行輪詢的時候,發現鏈接上有請求的話,纔開啓一個線程進行處理,也就是一個請求一個線程模式

在NIO的處理方式中,當一個請求來的話,開啓線程進行處理,可能會等待後端應用的資源(JDBC鏈接等),其實這個線程就被阻塞了,當併發上來的話,仍是會有BIO同樣的問題

 

3.AIO編程

Asynchronous IO: 異步非阻塞的編程方式。

與NIO不一樣,當進行讀寫操做時,只須直接調用API的read或write方法便可。這兩種方法均爲異步的,對於讀操做而言,當有流可讀取時,操做系統會將可讀的流傳入read方法的緩衝區,並通知應用程序;對於寫操做而言,當操做系統將write方法傳遞的流寫入完畢時,操做系統主動通知應用程序。便可以理解爲,read/write方法都是異步的,完成後會主動調用回調函數。在JDK1.7中,這部份內容被稱做NIO.2,主要在java.nio.channels包下增長了下面四個異步通道:AsynchronousSocketChannel、AsynchronousServerSocketChannel、AsynchronousFileChannel、AsynchronousDatagramChannel。

 

bio示例

server示例:

public class Server { public static void main(String[] args) { int port = genPort(args); ServerSocket server = null; ExecutorService service = Executors.newFixedThreadPool(50); try{ server = new ServerSocket(port); System.out.println("server started!"); while(true){ Socket socket = server.accept(); service.execute(new Handler(socket)); } }catch(Exception e){ e.printStackTrace(); }finally{ if(server != null){ try { server.close(); } catch (IOException e) { e.printStackTrace(); } } server = null; } } static class Handler implements Runnable{ Socket socket = null; public Handler(Socket socket){ this.socket = socket; } @Override public void run() { BufferedReader reader = null; PrintWriter writer = null; try{ reader = new BufferedReader( new InputStreamReader(socket.getInputStream(), "UTF-8")); writer = new PrintWriter( new OutputStreamWriter(socket.getOutputStream(), "UTF-8")); String readMessage = null; while(true){ System.out.println("server reading... "); if((readMessage = reader.readLine()) == null){ break; } System.out.println(readMessage); writer.println("server recive : " + readMessage); writer.flush(); } }catch(Exception e){ e.printStackTrace(); }finally{ if(socket != null){ try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } socket = null; if(reader != null){ try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } reader = null; if(writer != null){ writer.close(); } writer = null; } } } private static int genPort(String[] args){ if(args.length > 0){ try{ return Integer.parseInt(args[0]); }catch(NumberFormatException e){ return 9999; } }else{ return 9999; } } }

2.client示例:

public class Client { public static void main(String[] args) { String host = null; int port = 0; if(args.length > 2){ host = args[0]; port = Integer.parseInt(args[1]); }else{ host = "127.0.0.1"; port = 9999; } Socket socket = null; BufferedReader reader = null; PrintWriter writer = null; Scanner s = new Scanner(System.in); try{ socket = new Socket(host, port); String message = null; reader = new BufferedReader( new InputStreamReader(socket.getInputStream(), "UTF-8")); writer = new PrintWriter( socket.getOutputStream(), true); while(true){ message = s.nextLine(); if(message.equals("exit")){ break; } writer.println(message); writer.flush(); System.out.println(reader.readLine()); } }catch(Exception e){ e.printStackTrace(); }finally{ if(socket != null){ try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } socket = null; if(reader != null){ try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } reader = null; if(writer != null){ writer.close(); } writer = null; } }
相關文章
相關標籤/搜索