IO模型即輸入輸出模型,我們今天主要來聊的是java網路程式設計中的IO模型---BIO模型。
BIO即阻塞式IO,Blocking IO
blocking [ˈblɒkɪŋ]
v. 堵塞; 阻塞; 堵住(某人的路等); 擋住(某人的視線等); 妨礙; 阻礙;
那究竟什麼是阻塞呢?
這裡的阻塞和多執行緒並行控制中,對未持有鎖的執行緒進行同步阻塞是兩個概念。更多的是指停滯不前,由於未接受到指令,只能繼續等待的意思。
舉個經典的例子:(防盜連線:本文首發自http://www.cnblogs.com/jilodream/ )
西餐廳中,有1個服務員負責招待。客人進入餐廳中,服務員會根據客人的需要下單或上菜。
當客人A要求點菜時,服務員A開始下單。在這個過程中,客人中間發生了停頓,或者猶豫不決時,服務員只能阻塞等待,不能直接停滯,忙其他的事情。這就是所謂的阻塞。
這樣就會有一個問題,服務員只能做完一件事,再做一件事,當有客人B也有要求時,則不能並行執行,這裡就是前文中說的多執行緒同步。
這裡存在兩個"阻塞",
1、服務員等待指令,只能原地等候下一個指令。
2、由於服務員數量有限,即使其他客人有指令需要下發,服務員依然無法執行。
blocking 屬於前者,即服務員只能等待當前客人繼續傳送指令。
後者則屬於多執行緒同步問題,由於服務員數量有限,無法並行執行事務。
針對於後者,我們一般通過多執行緒來解決,而前者才是我們今天聊的重點。
大概明白了什麼是blocking阻塞之後,我們來看個由blocking IO實現的聊天工具:
Server端程式碼:
1 package com.example.demo.learn.tcp; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.io.OutputStream; 6 import java.net.ServerSocket; 7 import java.net.Socket; 8 import java.util.Scanner; 9 10 /** 11 * @discription 12 */ 13 14 public class TCPServer { 15 public static void main(String[] args) throws IOException { 16 ServerSocket serverSocket = new ServerSocket(9999); 17 while (true) { 18 Socket acceptSocket = serverSocket.accept();//注意這裡 19 ChatThread chatThread = new ChatThread(acceptSocket); 20 new Thread(chatThread).start(); 21 } 22 } 23 } 24 25 class ChatThread implements Runnable { 26 private Socket clientSocket; 27 28 ChatThread(Socket clientSocket) { 29 this.clientSocket = clientSocket; 30 } 31 32 @Override 33 public void run() { 34 try { 35 36 OutputStream os = clientSocket.getOutputStream(); 37 SayThread sayThread = new SayThread(os); 38 new Thread(sayThread).start(); 39 40 InputStream is = clientSocket.getInputStream(); 41 byte[] buffer = new byte[1024]; 42 int len = is.read(buffer);//注意這裡 43 while (len > 0) { 44 String msg = new String(buffer, 0, len); 45 System.out.println(""); 46 System.out.println("receive client msg:"); 47 System.out.println(msg); 48 System.out.println(""); 49 len = is.read(buffer);//注意這裡 50 } 51 clientSocket.close(); 52 53 } catch (Exception ex) { 54 //logs 55 } 56 57 } 58 } 59 60 class SayThread implements Runnable { 61 private OutputStream os; 62 63 SayThread(OutputStream outputStream) { 64 this.os = outputStream; 65 } 66 67 @Override 68 public void run() { 69 try { 70 os.write("server connect success!!!".getBytes()); 71 Scanner inputScanner = new Scanner(System.in); 72 while (true) { 73 String str = inputScanner.nextLine(); 74 os.write(str.getBytes()); 75 os.flush(); 76 } 77 78 } catch (Exception ex) { 79 //logs 80 } 81 82 } 83 }
Client端程式碼:
1 package com.zzzlei.zxxb.experience; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.io.OutputStream; 6 import java.net.Socket; 7 import java.util.Scanner; 8 9 /** 10 * @discription 11 */ 12 public class TCPClient { 13 public static void main(String[] args) throws IOException { 14 Socket clientSocket=new Socket("127.0.0.1",9999); 15 ChatThread chatThread = new ChatThread(clientSocket); 16 new Thread(chatThread).start(); 17 18 } 19 } 20 21 class ChatThread implements Runnable { 22 private Socket clientSocket; 23 24 ChatThread(Socket clientSocket) { 25 this.clientSocket = clientSocket; 26 } 27 28 @Override 29 public void run() { 30 try { 31 OutputStream os = clientSocket.getOutputStream(); 32 SayThread sayThread = new SayThread(os); 33 new Thread(sayThread).start(); 34 35 InputStream is = clientSocket.getInputStream(); 36 byte[] buffer = new byte[1024]; 37 int len = is.read(buffer);//注意這裡 38 while (len > 0) { 39 String msg = new String(buffer, 0, len); 40 System.out.println(""); 41 System.out.println("receive server msg :"); 42 System.out.println(msg); 43 System.out.println(""); 44 len = is.read(buffer);//注意這裡 45 } 46 clientSocket.close(); 47 48 } catch (Exception ex) { 49 //logs 50 } 51 52 } 53 } 54 55 class SayThread implements Runnable { 56 private OutputStream os; 57 58 SayThread(OutputStream outputStream) { 59 this.os = outputStream; 60 } 61 62 @Override 63 public void run() { 64 try { 65 os.write("client connect success!!!".getBytes()); 66 Scanner inputScanner = new Scanner(System.in); 67 while (true) { 68 String str = inputScanner.nextLine(); 69 os.write(str.getBytes()); 70 os.flush(); 71 } 72 73 } catch (Exception ex) { 74 //logs 75 } 76 77 } 78 }
效果如圖,我們可以通過這兩個程式進行聊天:
使用者端截圖:
伺服器端截圖:
下面就是BIO模型的簡示圖
伺服器端在建立好serverSocket之後,會等待使用者端socket的連線,當連線成功後,會在伺服器端和使用者端通過Socket進行通訊。
在這個程式(模型)中,存在兩個阻塞的點:(防盜連線:本文首發自http://www.cnblogs.com/jilodream/ )
1、伺服器在等待使用者端接入,也就是accept的地方。(伺服器端程式碼的 18行)
2、伺服器或使用者端在等待socket寫入指令的地方。(伺服器端程式碼的42,49行,使用者端程式碼37,44行)
如圖,執行緒雖然是RUNNING狀態,但是卻不繼續執行了:
想一想,這兩個地方是不是都是無法通過增加執行緒來實現?
BIO是Jdk 1.0 時就引入的網路程式設計模型,Jdk1.4之後,引入了NIO(我會在後文中詳細介紹),來解決阻塞問題,讓執行緒不再等待。
那有了NIO是不是就不再需要,BIO了呢?(防盜連線:本文首發自http://www.cnblogs.com/jilodream/ )
並不是,BIO的優點是可以通過增加執行緒進行業務隔離,邏輯清晰,編碼和模型實現也都非常簡單。
缺點則是如果想提高效能,需要增加多執行緒支撐,即使如此仍然存在阻塞點導致效能瓶頸上限比較低。
因此在資源滿足的情況下,連線數量少時,是比較推薦使用BIO的。
如果你覺得寫的不錯,歡迎轉載和點贊。 轉載時請保留作者署名jilodream/王若伊_恩賜解脫(部落格連結:http://www.cnblogs.com/jilodream/