【NIO引入】传统的IO模型

传统的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处理模型。



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

你可能感兴趣的:(server,nio)