NIO

2021-03-15  本文已影响0人  jiahzhon

简介

java NIO 与 IO的主要区别

IO NIO
面向流 面向缓存区
阻塞IO(Blocking IO) 非阻塞IO(Non Blocking IO)
(无) 选择器

缓存区存取数据的核心方法

缓存区的四个核心属性

public class test1 {

    public static void main(String[] args) {
        test2();
    }
    public static void test2(){
        String str = "abcde";

        //1. 分配一个指定大小的缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);

        System.out.println("----------allocate()---------");
        System.out.println(buf.position());//0
        System.out.println(buf.limit());//1024
        System.out.println(buf.capacity());//1024

        //2. 利用put()存入数据到缓冲区中
        buf.put(str.getBytes());

        System.out.println("----------put()---------");
        System.out.println(buf.position());//5
        System.out.println(buf.limit());//1024
        System.out.println(buf.capacity());//1024

        //3.切换读取数据模式
        buf.flip();

        System.out.println("----------put()---------");
        System.out.println(buf.position());//0
        System.out.println(buf.limit());//5
        System.out.println(buf.capacity());//1024

        //4.利用get()
        byte[] dst = new byte[buf.limit()];
        buf.get(dst);
        System.out.println(new String(dst,0,dst.length));

        System.out.println("----------get()---------");
        System.out.println(buf.position());//5
        System.out.println(buf.limit());//5
        System.out.println(buf.capacity());//1024

        //5.可重复读
        buf.rewind();
        System.out.println("----------rewind()---------");
        System.out.println(buf.position());//0
        System.out.println(buf.limit());//5
        System.out.println(buf.capacity());//1024

        //6.clear():清空缓冲区,但是缓冲区中的数据依然存在,但是出于“被遗忘”状态
        buf.clear();
        System.out.println("----------clear()---------");
        System.out.println(buf.position());//0
        System.out.println(buf.limit());//1024
        System.out.println(buf.capacity());//1024

        //mark:标记,表示记录当前position的位置。可以通过reset()恢复到mark位置
    }
}

直接缓冲区和非直接缓冲区

非直接缓存区:allocate()方法分配缓存区,将缓存区建立在JVM的内存中(堆,数组)

直接缓存区:allocateDirect()方法分配直接缓存区,将缓存区建立在物理内存中,可以提高效率

Channel

  1. Java针对支持通道的类提供了getChannel()方法:
  1. 在JDK 1.7中的NIO.2 针对各个通道提供了静态方法open()
  2. 在JDK 1.7中的NIO.2 的Files工具类的newByteChannel()
public class FileCopy {

    public static void main(String[] args) {
        test();
    }

    public static void test(){
        FileInputStream fis = null;
        FileOutputStream fos = null;

        //获取通道
        FileChannel inchannel = null;
        FileChannel outChannel = null;

        try {
            fis = new FileInputStream("D:/1.jpg");
            fos = new FileOutputStream("D:/2.jpg");

            inchannel = fis.getChannel();
            outChannel = fos.getChannel();

            //分配指定大小的缓冲区
            ByteBuffer buf = ByteBuffer.allocate(1024);

            //将通道中的数据存入到缓冲区中
            while (inchannel.read(buf) != -1){
                buf.flip(); //切换到读数据的模式
                //将缓冲区的数据写入到通道中
                outChannel.write(buf);
                buf.clear();//清空缓存区
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally{
            if(inchannel != null){
                try {
                    inchannel.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }

            if(outChannel != null){
                try {
                    outChannel.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }

            if(fis != null){
                try {
                    fis.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }

            if(fos != null){
                try {
                    fos.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
}
public class FileCopy2 {

    public static void main(String[] args) {
        test3();
    }

    private static void test3() {

        try {
            FileChannel inChannel = FileChannel.open(Paths.get("D:/1.jpg"), StandardOpenOption.READ);
            FileChannel outChannel = FileChannel.open(Paths.get("D:/3.jpg"), StandardOpenOption.WRITE, StandardOpenOption.READ,
            StandardOpenOption.CREATE_NEW);

            //内存映射文件
            MappedByteBuffer inMappedBuf = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
            MappedByteBuffer outMappedBuf = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());

            //直接对缓冲区进行数据的读写操作
            byte[] dst = new byte[inMappedBuf.limit()];
            inMappedBuf.get(dst);
            outMappedBuf.put(dst);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

分散(Scatter)和聚集(Gather)

public class FileCopy3 {

    public static void main(String[] args) {
        test4();
    }

    private static void test4() {
        try {
            RandomAccessFile raf1 = new RandomAccessFile("D:/3.txt","rw");

            //1.获取通道
            FileChannel channel1 = raf1.getChannel();

            //2.分配指定大小的缓冲区
            ByteBuffer buf1 = ByteBuffer.allocate(1);
            ByteBuffer buf2 = ByteBuffer.allocate(1024);

            //3.分散读取
            ByteBuffer[] bufs = {buf1,buf2};
            channel1.read(bufs);

            for (ByteBuffer byteBuffer : bufs) {
                byteBuffer.flip();
            }

            //聚集写入
            RandomAccessFile raf2 = new RandomAccessFile("D:/7.txt","rw");
            FileChannel channel2 = raf2.getChannel();
            channel2.write(bufs);


        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

Selector

Snipaste_2021-03-17_16-25-30.png

阻塞式网络IO实现

public class FileTest {

    public static void main(String[] args) {
        client();
    }

    //客户端
    private static void client() {

        try {
            //1.获取通道
            SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("9.236.37.131", 9898));

            FileChannel inChannel = FileChannel.open(Paths.get("D:/1.jpg"), StandardOpenOption.READ);

            //2.分配指定大小的缓冲区
            ByteBuffer buf = ByteBuffer.allocate(1024);

            //3.读取本地文件,并发送到服务端
            while (inChannel.read(buf) != -1){
                buf.flip();
                sChannel.write(buf);
                buf.clear();
            }
            //单向关闭自己的输出流,并未关闭连接,不写这个服务器端并不知道传输的图片结束了。一直卡在ssChannel.accept()
            sChannel.shutdownOutput();

            //接收服务器的反馈
            int len = 0;
            while ((len = sChannel.read(buf))!=-1){
                buf.flip();
                System.out.println(new String(buf.array(),0,len));
                buf.clear();
            }

            //4.关闭通道
            inChannel.close();
            sChannel.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
public class FileTest2 {

    public static void main(String[] args) {
        server();
    }

    //服务端
    private static void server() {
        try {
            //1.获取通道
            ServerSocketChannel ssChannel = ServerSocketChannel.open();

            FileChannel outChannel = FileChannel.open(Paths.get("D:/10.jpg"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);

            //2.绑定链接
            ssChannel.bind(new InetSocketAddress(9898));

            //3.获取客户端连接的通道
            SocketChannel sChannel = ssChannel.accept();

            //4.分配指定大小的缓冲区
            ByteBuffer buf = ByteBuffer.allocate(1024);

            //5.接收客户端的数据,并保存到本地
            while (sChannel.read(buf)!= -1){
                buf.flip();
                outChannel.write(buf);
                buf.clear();
            }

            //发送反馈给客户端
            buf.put("服务器接收数据成功".getBytes());
            buf.flip();
            sChannel.write(buf);
            //6.关闭通道
            sChannel.close();
            outChannel.close();
            ssChannel.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

非阻塞式网络IO实现

public class NonBlockClient {

    public static void main(String[] args) {
        client();
    }

    private static void client() {

        try {
            //1.获取通道
            SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("9.236.37.131", 9898));

            //2.切换非阻塞模式
            sChannel.configureBlocking(false);

            //3.分配指定大小的缓冲区
            ByteBuffer buf = ByteBuffer.allocate(1024);

            //4.发送数据给服务器
            Scanner scan = new Scanner(System.in);

            while (scan.hasNext()){
                String str = scan.next();
                buf.put((new Date().toString() + "\n"+str).getBytes());
                buf.flip();
                sChannel.write(buf);
                buf.clear();
            }

            //5.关闭通道
            sChannel.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
public class NonBlockServer {

    public static void main(String[] args) {
        server();
    }

    private static void server() {

        try {
            //1.获取通道
            ServerSocketChannel ssChannel = ServerSocketChannel.open();

            //2.切换非阻塞模式
            ssChannel.configureBlocking(false);

            //3.绑定链接
            ssChannel.bind(new InetSocketAddress(9898));

            //4.获取选择器
            Selector selector = Selector.open();

            //5.将通道注册到选择器上,并且指定“监听接收事件”
            //SelectionKey-----OP_ACCEPT,OP_CONNECT,OP_READ,OP_WRITE
            ssChannel.register(selector, SelectionKey.OP_ACCEPT);

            //6.轮询式的获取选择器上已经“准备就绪”的事件
            while (selector.select()>0){

                //7.获取当前选择器中所有注册的“选择键(已就绪的监听事件)”
                Iterator<SelectionKey> it = selector.selectedKeys().iterator();

                while (it.hasNext()){
                    //8. 获取准备“就绪”的事件
                    SelectionKey sk = it.next();

                    //9.判断具体是什么事件准备就绪
                    if(sk.isAcceptable()){
                        //10. 若“接收就绪”,获取客户端连接
                        SocketChannel sChannel = ssChannel.accept();

                        //11.切换非阻塞模式
                        sChannel.configureBlocking(false);

                        //12.将该通道注册到选择器上
                        sChannel.register(selector,SelectionKey.OP_READ);
                    }else if(sk.isReadable()){
                        //13. 获取当前选择器上“读就绪”状态的通道
                        SocketChannel sChannel = (SocketChannel)sk.channel();

                        //14. 读取数据
                        ByteBuffer buf = ByteBuffer.allocate(1024);

                        int len = 0;
                        while ((len = sChannel.read(buf))>0){
                            buf.flip();
                            System.out.println(new String(buf.array(),0,len));
                            buf.clear();
                        }
                    }

                    //15.取消选择键SelectionKey
                    it.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
上一篇 下一篇

猜你喜欢

热点阅读