什麼叫BIO , BIO 是 block input output 的縮寫 , 意思就是阻塞式的IO。這個是爲了區別後面的NIO , 有關NIO ,後面再作介紹。服務器
舉個簡單的例子:我在使用bio的socket網絡通訊中,會有下面的代碼:網絡
ServerSocket serverSocket = new ServerSocket(port); while (true) { Socket accept = serverSocket.accept(); InputStream inputStream = accept.getInputStream(); OutputStream outputStream = accept.getOutputStream(); byte[] readBytes = new byte[100]; inputStream.read(readBytes); System.out.println("===== " + new String(readBytes)); outputStream.write((System.currentTimeMillis() + "").getBytes()); accept.close(); }
在 inputStream.read(readBytes);這一行,若是socket連接沒有斷,他會一直等待,直到讀完socket的一次寫入的全部的數據,或者是byte指定大小的數據。在連接沒有斷開的狀況下,直到數據讀完,纔會執行下面的代碼。(這個是bio的關鍵部分)。多線程
因此,若是在服務端,若是是單線程的,上面的代碼,若是一個客戶端連接進來,沒有關閉,則第二個用戶連接,發送數據不會有相應的。socket
——————————ide
因此針對,bio 的socket,出現了下面三種模型:性能
這個比較簡單,直接寫demo :測試
public class BioSimpleServer { private static int port = 8888; private static void init() throws IOException { ServerSocket serverSocket = new ServerSocket(port); while (true) { Socket accept = serverSocket.accept(); InputStream inputStream = accept.getInputStream(); OutputStream outputStream = accept.getOutputStream(); byte[] readBytes = new byte[100]; inputStream.read(readBytes); String readString = new String(readBytes); System.out.println("===== " + readString); outputStream.write((System.currentTimeMillis() + "").getBytes()); if (readString != null && readString.trim().equalsIgnoreCase("quit")){ accept.close(); } } } public static void main(String[] args) throws IOException { init(); } }
這段代碼,只是一個簡單的demo , 沒有考慮發送數據的長度,直接經過100個byte接收了。若是客戶端,有數據過來,直接返回一個當前時間的毫秒數,若是請求的字符串是quit,則關閉鏈接。若是你採用telnet測試 , 只有第一個鏈接發送quit之後,第二個鏈接纔會有相應。ui
上面能夠看到,單線程模式的弊端是,一次只能服務一個客戶端,若是有多個客戶端過來連接,那就只能傻等,只有這個用戶關閉連接之後,第二個客戶端才能夠連接的上,爲了解決這個問題,出現了bio socket的多線程模式。線程
public class BioSimpleThread { private static int port = 8888; private static void init() throws IOException { ServerSocket serverSocket = new ServerSocket(port , 10); while (true) { final Socket accept = serverSocket.accept(); new Thread() { @Override public void run() { while (true){ try { InputStream inputStream = accept.getInputStream(); OutputStream outputStream = accept.getOutputStream(); byte[] readBytes = new byte[100]; inputStream.read(readBytes); String readString = new String(readBytes); System.out.println("===== " + readString); outputStream.write((System.currentTimeMillis() + "").getBytes()); if (readString != null && readString.trim().equalsIgnoreCase("quit")){ accept.close(); } } catch (Exception e) { e.printStackTrace(); } } } }.start(); } } public static void main(String[] args) throws IOException { init(); } }
代碼比較簡單,一看就懂,這裏添加了一個線程,只要有socket accept過來,就起一個線程,處理客戶端的請求,這樣能夠保證,能夠同時處理多個用戶請求的狀況。code
在看了多線程的bio 之後,咱們發現,只要有請求過來,就啓動一個線程,若是有10萬的客戶端輕輕,就會啓動10萬個線程 ,這樣就會出現cpu因爲多個線程切換,線程相互競爭,服務端會愈來愈慢,直到服務器被託跨的問題,
爲了不這種問題,咱們就須要規劃本身的用戶規模,考慮服務器的性能,單臺機器上面,最多能夠支持多少的在線用戶。採用線程池的模式。
public class BioThreadPool { private static int port = 8888; static ExecutorService cachedThreadPool = Executors.newFixedThreadPool(100); private static void init() throws IOException { ServerSocket serverSocket = new ServerSocket(port); while (true) { final Socket accept = serverSocket.accept(); cachedThreadPool.execute(new Thread() { @Override public void run() { try { InputStream inputStream = accept.getInputStream(); OutputStream outputStream = accept.getOutputStream(); byte[] readBytes = new byte[100]; inputStream.read(readBytes); String readString = new String(readBytes); System.out.println("===== " + readString); outputStream.write((System.currentTimeMillis() + "").getBytes()); if (readString != null && readString.trim().equalsIgnoreCase("quit")){ accept.close(); } } catch (Exception e) { if (accept != null){ try { accept.close(); } catch (IOException e1) { e1.printStackTrace(); } } e.printStackTrace(); } } }); } } public static void main(String[] args) throws IOException { init(); } }
這裏採用了 static ExecutorService cachedThreadPool = Executors.newFixedThreadPool(100); , 避免單臺服務器上面,啓動過多的線程 , 將服務器託跨。