什么叫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); , 避免单台服务器上面,启动过多的线程 , 将服务器托跨。