在上面一节中,服务端收到客户端的连接之后,都是new一个新的线程来处理客户端发送的请求,每次new 一个线程比较耗费系统资源,如果100万个客户端,我们就要创建100万个线程,相当的
耗费系统的资源,服务器是没有办法支持这样多的客户端进行连接的
我们可以采用线程池的方式来实现:提高
package bhz.bio2; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.net.UnknownHostException; public class Client { final static String ADDRESS = "127.0.0.1"; final static int PORT =8765; public static void main(String[] args) { Socket socket = null; BufferedReader in = null; PrintWriter out = null; try { socket = new Socket(ADDRESS, PORT); in = new BufferedReader(new InputStreamReader(socket.getInputStream())); out = new PrintWriter(socket.getOutputStream(), true); out.println("Client request"); String response = in.readLine(); System.out.println("Client:" + response); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { if(in != null){ try { in.close(); } catch (Exception e1) { e1.printStackTrace(); } } if(out != null){ try { out.close(); } catch (Exception e2) { e2.printStackTrace(); } } if(socket != null){ try { socket.close(); } catch (Exception e3) { e3.printStackTrace(); } } socket = null; } } }
package bhz.bio2; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class HandlerExecutorPool { private ExecutorService executor; public HandlerExecutorPool(int maxPoolSize, int queueSize){ this.executor = new ThreadPoolExecutor( Runtime.getRuntime().availableProcessors(), maxPoolSize, 120L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(queueSize)); } public void execute(Runnable task){ this.executor.execute(task); } }
package bhz.bio2; import java.io.BufferedReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; public class Server { final static int PORT = 8765; public static void main(String[] args) { ServerSocket server = null; BufferedReader in = null; PrintWriter out = null; try { server = new ServerSocket(PORT); System.out.println("server start"); Socket socket = null; HandlerExecutorPool executorPool = new HandlerExecutorPool(50, 1000); while(true){ socket = server.accept(); executorPool.execute(new ServerHandler(socket)); } } catch (Exception e) { e.printStackTrace(); } finally { if(in != null){ try { in.close(); } catch (Exception e1) { e1.printStackTrace(); } } if(out != null){ try { out.close(); } catch (Exception e2) { e2.printStackTrace(); } } if(server != null){ try { server.close(); } catch (Exception e3) { e3.printStackTrace(); } } server = null; } } }
package bhz.bio2; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; public class ServerHandler implements Runnable { private Socket socket; public ServerHandler (Socket socket){ this.socket = socket; } @Override public void run() { BufferedReader in = null; PrintWriter out = null; try { in = new BufferedReader(new InputStreamReader(this.socket.getInputStream())); out = new PrintWriter(this.socket.getOutputStream(), true); String body = null; while(true){ body = in.readLine(); if(body == null) break; System.out.println("Server:" + body); out.println("Server response"); } } catch (Exception e) { e.printStackTrace(); } finally { if(in != null){ try { in.close(); } catch (Exception e1) { e1.printStackTrace(); } } if(out != null){ try { out.close(); } catch (Exception e2) { e2.printStackTrace(); } } if(socket != null){ try { socket.close(); } catch (Exception e3) { e3.printStackTrace(); } } socket = null; } } }
阻塞模式:列如客户端请求服务器的数据,服务器如果返回10个字节的数据给客户端,如果网络较差,客户端就必须一致等待着指定10个字节发送完成,就一直阻塞着
非阻塞模式nio模式:应用程序无需等待可以直接获得服务器端的数据,无效等待,线程不会阻塞
阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。
同步和异步:
A. 同步
所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。
按照这个定义,其实绝大多数函数都是同步调用(例如sin isdigit等)。
但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务。
最常见的例子就是 SendMessage。
该函数发送一个消息给某个窗口,在对方处理完消息之前,这个函数不返回。
当对方处理完毕以后,该函数才把消息处理函数所返回的值返回给调用者。
B. 异步
异步的概念和同步相对。
当一个异步过程调用发出后,调用者不会立刻得到结果。
实际处理这个调用的部件是在调用发出后,
通过状态、通知来通知调用者,或通过回调函数处理这个调用。
以 Socket为例,
当一个客户端通过调用 Connect函数发出一个连接请求后,调用者线程不用等待结果,可立刻继续向下运行。
当连接真正建立起来以后,socket底层会发送一个消息通知该对象。
C. 三种返回结果途径
执行部件和调用者可以通过三种途径返回结果:
a. 状态、
b. 通知、
c. 回调函数。
总结:阻塞是针对当前的线程
同步和异步:值得的函数的返回值,同步调用者会一直等待值得服务器返回值,异步是调用该函数不会立刻得到返回值,服务器通知、或者回调的方法通知调用者
getInputStream和getOutputStream都是同步并且阻塞的模型
jdk1.7版本之前nio是同步非阻塞的,在1.7以后实现了异步非阻塞的