NIO的前世今生

来源:互联网 发布:霍建华扒皮知乎 编辑:程序博客网 时间:2024/06/05 11:04

在面试中NIO的东西还是有很多会被面到,那么NIO是什么?

从古老的套接字(socket)说起,传统的socket编程为新启动一个服务,阻塞到这,一个Client端连接过来,new 一个Thread 去处理

总结顺便学习一下IDEA ,破解版http://xidea.online,

执行idea时出现"无效的源发行版"  file -> settings -> build,exception,deployment -> compiler -> java compiler

主函数  psvm +tab键   输出 so+tab

1.BIO 即原始的同步阻塞式
服务端
package com.wpx.bio;import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;/** * 服务端 通过一个端口对外提供服务 * 当一个Client端连接后通过新启动一个线程去处理 */class Server {    final static int PORT = 8888;    public static void main(String[] args) {        ServerSocket ss = null;        try {            ss = new ServerSocket(PORT);            System.out.println("Server start....");            Socket socket = ss.accept();            //new Thread Handler            new Thread(new ServerHandler(socket)).start();        } catch (IOException e) {            e.printStackTrace();        }finally {            if(ss != null){                try {                    ss.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            ss = null;        }    }}
服务端处理类
package com.wpx.bio;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.PrintWriter;import java.net.Socket;/**\ * 服务端处理类需要jichengThread或者实现Runnable */public class ServerHandler implements  Runnable{private Socket socket ;/** * 构造 Alt+Insert * @param socket */public ServerHandler(Socket socket){this.socket = socket;}/** * Ctrl+O */@Overridepublic 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 {if(in != null){try {in.close();} catch (IOException e) {e.printStackTrace();}}if(out != null){try {out.close();} catch (Exception e) {e.printStackTrace();}}if(socket != null){try {socket.close();} catch (IOException e) {e.printStackTrace();}}socket = null;}}}
客户端
package com.wpx.bio;import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;/** * 服务端 通过一个端口对外提供服务 * 当一个Client端连接后通过新启动一个线程去处理 */class Server {    final static int PORT = 8888;    public static void main(String[] args) {        ServerSocket ss = null;        try {            ss = new ServerSocket(PORT);            System.out.println("Server start....");            Socket socket = ss.accept();            //new Thread Handler            new Thread(new ServerHandler(socket)).start();        } catch (IOException e) {            e.printStackTrace();        }finally {            if(ss != null){                try {                    ss.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            ss = null;        }    }}
因为每一个客户端的连接都会新启动一个线程,给服务器造成了巨大压力,在没有NIO之前,通过使用线程池+一个阻塞的队列来缓解服务器压力

服务端
package com.wpx.bio2;import java.io.BufferedReader;import java.io.PrintWriter;import java.net.ServerSocket;import java.net.Socket;import java.nio.Buffer;/** * 与开始处理思路大体一致 * 不同的是新的客户端连接后偶交由线程池处理 */public class Server {    final  static int PORT=8888;    public static void main(String[] args) {        ServerSocket ss=null;        BufferedReader in=null;        PrintWriter out=null;            try{                ss=new ServerSocket(PORT);                System.out.println("Server satrt.....");                Socket socket = null;                HandlerExecutorPool executorPool = new HandlerExecutorPool(50, 1000);                while(true){                    socket = ss.accept();                    executorPool.execute(new ServerHandler(socket));                }            }catch (Exception e){                e.printStackTrace();                new RuntimeException("服务端异常");            }finally {                if(in != null){                    try {                        in.close();                    } catch (Exception e1) {                        e1.printStackTrace();                    }                }                if(out != null){                    try {                        out.close();                    } catch (Exception e2) {                        e2.printStackTrace();                    }                }                if(ss != null){                    try {                        ss.close();                    } catch (Exception e3) {                        e3.printStackTrace();                    }                }                ss = null;            }    }}
线程池
package com.wpx.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 com.wpx.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;        }    }}
客户端
package com.wpx.bio2;import java.io.BufferedReader;import java.io.InputStreamReader;import java.io.PrintWriter;import java.net.Socket;public class Client {    final static String ADDRESS = "127.0.0.1";    final static int PORT =8888;    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());            out.print("Client Request");           String  response= in.readLine();            System.out.println("Clent"+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;        }    }}
2.NIO1.0位同步非阻塞

通道:Channel,通道与流的不同之处在于通道是双向的。而流只是在一个方向上移动(一个流必须是 InputStream 或者 OutputStream 的子类), 而 通道 可以用于读、写或者同时用于读写。

缓冲区:原来传输为直练传输,现在加入缓冲区,类似一个快递中转站,快递到了通知你去哪,而不需要一直等待看着快递来
package com.wpx.nio;import java.nio.IntBuffer;public class BufferTest {    public static void main(String[] args) {        //创建指定长度的缓存区  Buffre不仅仅只有Int类型的,仅依次为了例        //创建指定长度        /**         * 生成返回值类型 Alt+Enter         */        IntBuffer buf = IntBuffer.allocate(10);        buf.put(1);// position位置:0 - > 1        buf.put(2);// position位置:1 - > 2        buf.put(3);// position位置:2 - > 3        //把位置复位为0,也就是position位置:3 - > 0        buf.flip();        System.out.println("容量为 "+buf.capacity()); //容量初始化后不允许发生改变(warp方法包裹数组除外)        System.out.println("限制为"+buf.limit());  //实际装载元素        System.out.println("获取下标为1的元素:" + buf.get(1));        System.out.println("get(index)方法,position位置不改变:" + buf);        buf.put(1, 4);        System.out.println("put(index, change)方法,position位置不变:" + buf);;        for (int i = 0; i < buf.limit(); i++) {            //调用get方法会使其缓冲区位置(position)向后递增一位            System.out.print(buf.get() + "\t");        }        System.out.println("buf对象遍历之后为: " + buf);        // 2 wrap方法使用        /**         //  wrap方法会包裹一个数组: 一般这种用法不会先初始化缓存对象的长度,因为没有意义,最后还会被wrap所包裹的数组覆盖掉。          //  并且wrap方法修改缓冲区对象的时候,数组本身也会跟着发生变化。                              int[] arr = new int[]{1,2,5};         IntBuffer buf1 = IntBuffer.wrap(arr);         System.out.println(buf1);         IntBuffer buf2 = IntBuffer.wrap(arr, 0 , 2);         //这样使用表示容量为数组arr的长度,但是可操作的元素只有实际进入缓存区的元素长度         System.out.println(buf2);         */        // 3 其他方法        /**         IntBuffer buf1 = IntBuffer.allocate(10);         int[] arr = new int[]{1,2,5};         buf1.put(arr);         System.out.println(buf1);         //一种复制方法         IntBuffer buf3 = buf1.duplicate();         System.out.println(buf3);         //设置buf1的位置属性         //buf1.position(0);         buf1.flip();         System.out.println(buf1);         System.out.println("可读数据为:" + buf1.remaining());         int[] arr2 = new int[buf1.remaining()];         //将缓冲区数据放入arr2数组中去         buf1.get(arr2);         for(int i : arr2){         System.out.print(Integer.toString(i) + ",");         }         */    }    }

selector:选择器,他是NIO编程的基础,多路复用器提供选择已经再绪的任务的能力

Selector会不断的轮询注册在其上的通道(Channel),如果如果某个通道发生了读写操作,这个通道就处于就绪状态,会被Selector轮询出来,然后通过SelectionKey可以取得就绪的Channel集合,从而进行后续的IO操作

一个多路复用器(Selector)可以负责成千上万的Channel通道没有上限,这也是JDK使用epoll代替的了传统的select实现,获得连接句柄没有限制,这也就意味着我们只要一个线程负责Selector的轮询,就可以接入成千上万的客户端,这是JDK NIO库的巨大进步



服务端

package com.wpx.nio;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.*;import java.util.Iterator;/** *必须实现Runnable接口 * 新启动一个线程让Selector处于一种轮询的状态 * * @author wangpx */public class Server implements Runnable{    //多路复用器    private Selector selector;    //建立缓存区  2的倍数更高效    private ByteBuffer readBuf=ByteBuffer.allocate(1024);    private ByteBuffer writeBuf=ByteBuffer.allocate(1024);    public Server(int port) {        try{            //打开多路复用器            this.selector=Selector.open();            //打开服务通道            ServerSocketChannel ssc = ServerSocketChannel.open();            //将其设置为非阻塞莫斯            ssc.configureBlocking(false);            //绑定地址            ssc.bind( new InetSocketAddress(port));            //将服务器通道注册到多路复用器上,并且监听阻塞事件            ssc.register(this.selector, SelectionKey.OP_ACCEPT);            System.out.println("Server start and port is :" +port);        }catch(Exception e){            e.printStackTrace();        }    }    @Override    public void run() {        while(true) {            try {                // 让多路复用器开始监听                this.selector.select();                //返回多路复用器已经被选择的结果集                Iterator<SelectionKey> keys = this.selector.selectedKeys().iterator();                //进行遍历                while(keys.hasNext()) {                    //获取一个选择的元素                    SelectionKey key = keys.next();                    //直接从容器中移除就可以了                    keys.remove();                    //如果是有效的                    if(key.isValid()){                        //如果为阻塞状态                        if(key.isAcceptable()){                            this.accept(key);                        }                        //如果是可读状态                        if (key.isReadable()){                            this.read(key);                        }                        //如果是可写状态                        if(key.isWritable()){                            this.write(key);                        }                    }                }            } catch (IOException e) {                e.printStackTrace();            }        }    }    private void write(SelectionKey key) {    }    private void read(SelectionKey key) {        try{            //清空缓冲区的数据            this.readBuf.clear();            //获取之前注册的socket通道对象            SocketChannel sc= (SocketChannel) key.channel();            int read = sc.read(this.readBuf);            if (read == -1){                key.channel().close();                key.cancel();                return ;            }            //有数据则进行读取,读取前要复位方法,详情见上面的demo            this.readBuf.flip();            //根据缓冲区的数据长度创建相对的byte数组,接受缓冲区的数据            byte[] bytes=new byte[this.readBuf.remaining()];            //接受缓冲区数据            this.readBuf.get(bytes);            //打印结果            String body=new String(bytes).trim();            System.out.println("Server: "+body);        }catch(Exception e){            e.printStackTrace();        }    }    private void accept(SelectionKey key) {        try{            //获取服务通道            ServerSocketChannel ssc = (ServerSocketChannel) key.channel();            //执行阻塞方法            SocketChannel sc = ssc.accept();            //设置阻塞模式            sc.configureBlocking(false);            //注册到多路复用器上并设置读取标识别            sc.register(this.selector,SelectionKey.OP_READ);        }catch(Exception e){            e.printStackTrace();        }    }    public static void main(String[] args) {        new Thread(new Server(8888)).start();    }}
客户端

package com.wpx.nio;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.SocketChannel;public class Client {    public static void main(String[] args) {        InetSocketAddress address=new InetSocketAddress("127.0.0.1",8888);        //声明连接通道        SocketChannel sc=null;        //建立缓冲区        ByteBuffer buf = ByteBuffer.allocate(1024);        try{            //打开通道             sc = SocketChannel.open();             sc.connect(address);             while(true) {                //定义一个字节数组,然后使用系统录入功能                 byte[] bytes=new byte[1024];                 System.in.read(bytes);                //把数据放到缓冲                 buf.put(bytes);                 //对缓冲区进行复位                 buf.flip();                 //写出数据                 sc.write(buf);                 //清空缓冲数据                 buf.clear();             }        }catch (Exception e){            e.printStackTrace();        }finally {            if ( sc!=null){                try{                    sc.close();                }catch(Exception  e){                    sc=null;                }            }        }    }}

3.AIO 异步阻塞式

将复杂的Selector屏蔽,并且引入了线程组

服务端

package com.wpx.aio;import java.net.InetSocketAddress;import java.nio.channels.AsynchronousChannelGroup;import java.nio.channels.AsynchronousServerSocketChannel;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class Server {    //线程池    private ExecutorService executorService;    //线程组    private AsynchronousChannelGroup asynchronousChannelGroup;    //服务器通道    public AsynchronousServerSocketChannel asynchronousServerSocketChannel;    public Server(int  port) {      try{          //创建一个缓存池          executorService= Executors.newCachedThreadPool();          //创建一个线程组          asynchronousChannelGroup=AsynchronousChannelGroup.withCachedThreadPool(executorService,1);          //创建服务器通道          asynchronousServerSocketChannel=AsynchronousServerSocketChannel.open(asynchronousChannelGroup);          //进行绑定          asynchronousServerSocketChannel.bind(new InetSocketAddress(port));          System.out.println("server start , port : " + port);          //进行阻塞          asynchronousServerSocketChannel.accept(this, new ServerCompletionHandler());          //一直阻塞 不让服务器停止          Thread.sleep(Integer.MAX_VALUE);      }catch (Exception e){          e.printStackTrace();      }    }    public static void main(String[] args) {        Server s=new Server(8888);    }}

处理类

package com.wpx.aio;import java.nio.ByteBuffer;import java.nio.channels.AsynchronousSocketChannel;import java.nio.channels.CompletionHandler;import java.util.concurrent.ExecutionException;public class ServerCompletionHandler implements CompletionHandler<AsynchronousSocketChannel,com.wpx.aio.Server>{    @Override    public void completed(AsynchronousSocketChannel asc, Server attachment) {        //当有下一个客户端接入的时候 直接调用Server的accept方法,这样反复执行下去,保证多个客户端可以阻塞        attachment.asynchronousServerSocketChannel.accept(attachment,this);        read(asc);    }    private void read(final AsynchronousSocketChannel asc) {        //读取数据        ByteBuffer buf = ByteBuffer.allocate(1024);        asc.read(buf, buf, new CompletionHandler<Integer, ByteBuffer>() {            @Override            public void completed(Integer resultSize, ByteBuffer attachment) {                //进行读取之后,重置标识位                attachment.flip();                //获得读取的字节数                System.out.println("Server -> " + "收到客户端的数据长度为:" + resultSize);                //获取读取的数据                String resultData = new String(attachment.array()).trim();                System.out.println("Server -> " + "收到客户端的数据信息为:" + resultData);                String response = "服务器响应, 收到了客户端发来的数据: " + resultData;                write(asc, response);            }            @Override            public void failed(Throwable exc, ByteBuffer attachment) {                exc.printStackTrace();            }        });    }    private void write(AsynchronousSocketChannel asc, String response) {        try {            ByteBuffer buf = ByteBuffer.allocate(1024);            buf.put(response.getBytes());            buf.flip();            asc.write(buf).get();        } catch (InterruptedException e) {            e.printStackTrace();        } catch (ExecutionException e) {            e.printStackTrace();        }    }    @Override    public void failed(Throwable exc, Server attachment) {        exc.printStackTrace();    }}

客户端

package com.wpx.aio;import java.io.UnsupportedEncodingException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.AsynchronousSocketChannel;import java.util.concurrent.ExecutionException;public class Client implements Runnable{    private AsynchronousSocketChannel asc ;    public Client() throws Exception {        asc = AsynchronousSocketChannel.open();    }    public void connect(){        asc.connect(new InetSocketAddress("127.0.0.1", 8888));    }    public void write(String request){        try {            asc.write(ByteBuffer.wrap(request.getBytes())).get();            read();        } catch (Exception e) {            e.printStackTrace();        }    }    private void read() {        ByteBuffer buf = ByteBuffer.allocate(1024);        try {            asc.read(buf).get();            buf.flip();            byte[] respByte = new byte[buf.remaining()];            buf.get(respByte);            System.out.println(new String(respByte,"utf-8").trim());        } catch (InterruptedException e) {            e.printStackTrace();        } catch (ExecutionException e) {            e.printStackTrace();        } catch (UnsupportedEncodingException e) {            e.printStackTrace();        }    }    @Override    public void run() {        while(true){        }    }    public static void main(String[] args) throws Exception {        Client c1 = new Client();        c1.connect();        Client c2 = new Client();        c2.connect();        Client c3 = new Client();        c3.connect();        new Thread(c1, "c1").start();        new Thread(c2, "c2").start();        new Thread(c3, "c3").start();        Thread.sleep(1000);        c1.write("c1 aaa");        c2.write("c2 bbbb");        c3.write("c3 ccccc");    }}


原创粉丝点击