天天看点

从BIO开始,讲到NIO,AIO(1)一BIO(同步阻塞)二NIO(同步不阻塞)

一BIO(同步阻塞)

java的原生io是一种BIO,也就是阻塞io,下面我们将要简单的模仿一下BIO。

BIO

public class BIO {

    public static void main(String[] args) throws Exception{

        ServerSocket server = new ServerSocket(1999);

        while(true) {

            Socket client = server.accept();

            Thread thread = new Thread(new Runnable() {

                @Override

                public void run() {

                    handler(client);

                }

            });

            thread.start();

        }

    }

    public static void handler(Socket client){

        InputStream input = null;

        try{

            input = client.getInputStream();

            byte[] bytes = new byte[1024];

            input.read(bytes);

            System.out.println(new String(bytes));

            input.close();

        }catch(Exception e) {

            //处理异常

        }finally {

            try {

                input.close();

            } catch (IOException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

        }

    }

}

当然,我们只是简单模仿一下BIO,

这里我们清除,BIO是通过客户端来一个请求,服务器端就创造一个线程来处理请求,这样就是请求和线程是一对一关系,

但是要考虑两个问题,一个线程开销,另一个是阻塞问题。

1.线程开销问题

多线程编程并不是开启线程越多越好,线程切换是需要考虑线程上下文切换的消耗,当切换的消耗太大时,就证明线程太多了。

来一个请求就开一个线程,这就会造成问题,当请求很多,一个是服务器线程上下文切换造成cpu资源浪费,第二个是会超出服务器服务能力。

2阻塞问题

在上面这段模仿的BIO代码上有两处是阻塞的,一个 server.accept();,另一个是read方法,这就会造成当前线程阻塞,浪费cpu资源。

1.对BIO改进的方案,BIO开启线程数时根据请求来控制的,可以使用线程池技术来优化,但是效果是不太好的,

2.第二个方案是还是在线程数量上做文章,BIO的线程数时不可控的,但是我们想要把他变成服务器可以控制的,

3.第三个方法是在阻塞的问题上,主要是由于线程阻塞,不能干其他事,这主要是对文件等一些读写操作造成的,那我们能不能利用这样的阻塞时间。

二NIO(同步不阻塞)

个人觉得,NIO是针对改进方案的2,3条来改进,

NIO的工作模式,selector是一个轮询器,当轮询到的channel有需要处理的事件,就处理,处理完了接着轮询。也就会一个selector处理多个channel,这里的channel可以看做是一个socket,也就是一个请求。channel数据是buffer,buffer是一个块,通俗来讲就是数组,可读写。普遍量一个线程只有一个selector,这样我们可以控制开多少个线程,一个selector可以处理多个channel,而且是不阻塞的,这样就可以利用到BIO中线程阻塞的时间。

NIO的三大组件Selector,Channel,Buffer

首先我们来简单了解一下Buffer,buffer有四个重要变量,一个是mark 是一个变量,初始值为-1,一个是position 这是是接下来需要读写的位置,一个capacity,这个是buffer的容量,buffer底层其实就是一个数组,capacity就是数组的大小,最后一个是limit,这个位置是可以读或者写到最末位置。读写的切换必须调用flip方法。

除boolean 外,其他java的基本数据类型都有对应的buffer,比如ByteBuffer,IntBuffer等。