【NIO引入】传统的IO模型

来源:互联网 发布:企业网络防火墙 编辑:程序博客网 时间:2024/06/07 00:26

传统的BIO模型

服务端代码:

public class Server1 {    public static void main(String[] args) {        System.out.println("服务器启动...");        Socket client = null;        BufferedReader in = null;        PrintWriter pw = null;        BufferedReader br = null;        try {            ServerSocket server = new ServerSocket(8080);            while (true) {                //阻塞等待                client = server.accept();                in = new BufferedReader(new InputStreamReader(client.getInputStream()));                pw = new PrintWriter(client.getOutputStream());                br = new BufferedReader(new InputStreamReader(System.in));                String s = in.readLine();                System.out.println("客户端: " +s);                pw.println(br.readLine());                pw.flush();                if("end".equals(s)){                    System.out.println("服务器将关闭连接");                    break;                }            }        } catch (IOException e) {            e.printStackTrace();        }finally {            try {                br.close();                pw.close();                in.close();                client.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }}

客户端代码:

public class Client1 {    public static void main(String[] args) {        System.out.println("客户端1启动...");        System.out.println("当输入 \" end \" 时, 客户端将终止\n");        Socket server = null;        Socket client = null;        BufferedReader in = null;        PrintWriter pw = null;        BufferedReader br = null;        try {            while (true) {                server = new Socket(InetAddress.getLocalHost(), 8080);                br = new BufferedReader(new InputStreamReader(System.in));                pw = new PrintWriter(server.getOutputStream());                in = new BufferedReader(new InputStreamReader(server.getInputStream()));                String str = br.readLine();                pw.println(str);                pw.flush();                if("end".equals(str)){                    System.out.println("客户端将关闭连接");                    break;                }                System.out.println("服务器: "+in.readLine());            }        }catch (IOException e) {                e.printStackTrace();            }finally {            try {                in.close();                pw.close();                br.close();                server.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }}

分析:采用BIO通信模型的服务端。通常由一个独立的Acceptor线程负责监听客户端的连接,它接收到客户端的连接请求后为每一个客户端创建一个新的线程,处理完成后销毁线程,这是典型的一对一模型。

缺点:当客户端并发访问量增加后,服务端不得不创建多个线程,但线程是Java虚拟机非常宝贵的资源,系统会发生堆栈溢出、创建新线程失败等问题。

解决方法:创建一个线程池来存放新建的客户端连接(即伪异步模型)。

伪异步IO模型

服务端代码:

public class Server2 {    public static void main(String[] args) throws IOException {        ServerSocket server = null;        try {            server = new ServerSocket(8080);            System.out.println("服务端启动...... ");            Socket socket = null;            TimeServerHandlerExecutePool singleExecutor = new TimeServerHandlerExecutePool(                    50, 10000);// 创建IO任务线程池            while (true) {                socket = server.accept();                singleExecutor.execute(new TimeServerHandler(socket));            }        } finally {            if (server != null) {                server.close();                server = null;            }        }    }}class TimeServerHandlerExecutePool {    private ExecutorService executor;    public TimeServerHandlerExecutePool(int maxPoolSize, int queueSize) {        executor = new ThreadPoolExecutor(Runtime.getRuntime()                .availableProcessors(), maxPoolSize, 120L, TimeUnit.SECONDS,                new ArrayBlockingQueue<Runnable>(queueSize));    }    public void execute(java.lang.Runnable task) {        executor.execute(task);    }}class TimeServerHandler implements Runnable {    private Socket socket;    public TimeServerHandler(Socket socket) {        this.socket = socket;    }    @Override    public void run() {        BufferedReader br = null;        PrintWriter pw = null;        try {            br = new BufferedReader(new InputStreamReader(                    this.socket.getInputStream()));            pw = new PrintWriter(socket.getOutputStream());            br = new BufferedReader(new InputStreamReader(System.in));            String s = br.readLine();            System.out.println("客户端: " +s);            pw.println(br.readLine());            pw.flush();        } catch (Exception e) {            if (br != null) {                try {                    br.close();                } catch (IOException e1) {                    e1.printStackTrace();                }            }            if (pw != null) {                pw.close();                pw = null;            }            if (this.socket != null) {                try {                    this.socket.close();                } catch (IOException e1) {                    e1.printStackTrace();                }                this.socket = null;            }        }    }}

客户端代码不变(同BIO)

分析:伪异步IO虽然采用了线程池,避免了一请求一连接的问题,但它的底层还是采用同步阻塞模型,其最本质的问题在于,严重依赖于线程。但线程是很”贵”的资源
,因为线程的创建和销毁成本很高。

缺点:

  • 由于线程池采用阻塞队列实现。当队列积满后,后续队列的操作将被阻塞。

  • 线程的切换成本高的。

总结:

当面对十万甚至百万级连接的时候,传统的BIO模型是无能为力的。这就需要一种更高效的I/O处理模型。



本人才疏学浅,若有错误,请指出
谢谢!