BIO/NIO/AIO的区分

故事扮演 提交于 2019-11-29 01:43:26

BIO:同步阻塞IO(平常说的IO指的是BIO)
NIO:同步非阻塞IO
AIO:异步非阻塞IO

io操作分为两部分,发起io请求,和io数据读写。

阻塞、非阻塞主要是针对线程发起io请求后,是否立即返回来定义的,立即返回称为非阻塞io,否则称为阻塞io。

同步、异步主要针对io数据读写来定义的,读写数据过程中不阻塞线程称为异步io,否则,称为同步io。

一、BIO

线程发起io请求后,一直阻塞(阻塞io),直到数据就绪后,用户线程将数据写入socket空间,或从socket空间读取数据(同步)。

JDK5之前, JDK的IO模式只有BIO(同步阻塞)。

问题:因为阻塞的存在, 需对每个请求开启一个线程. 过多的线程切换影响操作系统性能。

解决:使用线程池, 处理不过来的放入队列, 再处理不过来的会触发其他机制。

问题::超过线程池数量的请求需要等待。

1. 客户端

public class Client {

    final static String ADDRESS = "127.0.0.1";
    final static int PORT = 8765;
    
    public static void main(String[] args) throws IOException {
        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);  // true自动flush
            //向服务器端发送数据
            out.println("来自客户端的请求");
            //从服务端接收数据
            String response = in.readLine();  // 阻塞
            System.out.println("Client获取数据: " + response);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            out.close();
            in.close();
            socket.close();
        }
    }
}
View Code

2. 服务端

(1) 一个请求~一个线程

public class Server {
    final static int PROT = 8765;
    public static void main(String[] args) throws IOException {
        ServerSocket server = null;
        try {
            server = new ServerSocket(PROT);
            System.out.println("server start");
            while(true){
                Socket socket = server.accept();  //监听 阻塞 , socket底层会新建线程处理与客户端的三次握手
                //建立线程处理获取的 socket
                new Thread(new ServerHandler(socket)).start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            server.close();
        }
    }
}

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("来自服务器的响应");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
                in.close();
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
View Code

(2) 线程池处理

public class Server {

    final static int PORT = 8765;

    public static void main(String[] args) throws IOException {
        ServerSocket server = null;
        try {
            server = new ServerSocket(PORT);
            System.out.println("server start");
            HandlerExecutorPool executorPool = new HandlerExecutorPool(50, 1000);
            while(true){
                Socket socket = server.accept();
                executorPool.execute(new ServerHandler(socket));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            server.close();
        }
    }
}

class HandlerExecutorPool {
    private ExecutorService executor;
    public HandlerExecutorPool(int maxPoolSize, int queueSize){
        this.executor = new ThreadPoolExecutor( // 带阻塞队列的线程池
                Runtime.getRuntime().availableProcessors(),  // 初始线程数
                maxPoolSize,        // 线程数上限   如果要处理请求的Runnable对象装满了队列, 则提高现有线程数
                120L,               // 如在120个时间颗粒内某线程是空闲的, 将被回收
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(queueSize)  // 存放处理请求的Runnable对象
        );
    }
    public void execute(Runnable task){
        this.executor.execute(task);
    }
}

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("来自服务器的响应");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
                in.close();
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
View Code

二、IO多路复用(NIO)

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!