NIO的学习

jvava nio的概念

    non blocking io ,java非阻塞读写方式,NIO支持面向缓冲区的,基于通道的iO的操作。NIO将以更加高效的方式进行文件的读写操作。

一、NIO和IO的区别

①传统的io是面向流的单向输入输出

    

②nio是面向缓冲区(火车)和通道(铁路)

 

    

 

    缓存区是一个装货和卸货的火车,所以可以双向流动使用,是非阻塞的

    

二、NIO中缓冲区(Buffer)的几个重要属性,缓冲区的数据获取及直接和非直接缓冲区

直接与非直接缓冲区

 

/*

 * 一、缓冲区(Buffer):在 Java NIO 中负责数据的存取。缓冲区就是数组。用于存储不同数据类型的数据

 * 

 * 根据数据类型不同(boolean 除外),提供了相应类型的缓冲区:

 * ByteBuffer

 * CharBuffer

 * ShortBuffer

 * IntBuffer

 * LongBuffer

 * FloatBuffer

 * DoubleBuffer

 * 

 * 上述缓冲区的管理方式几乎一致,通过 allocate() 获取缓冲区

 * 

 * 二、缓冲区存取数据的两个核心方法:

 * put() : 存入数据到缓冲区中

 * get() : 获取缓冲区中的数据

 * 

 * 三、缓冲区中的四个核心属性:

 * capacity : 容量,表示缓冲区中最大存储数据的容量。一旦声明不能改变。

 * limit : 界限,表示缓冲区中可以操作数据的大小。(limit 后数据不能进行读写)

 * position : 位置,表示缓冲区中正在操作数据的位置。

 * 

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

 * 

 * 0 <= mark <= position <= limit <= capacity

 * 

 * 四、直接缓冲区与非直接缓冲区:

 * 非直接缓冲区:通过 allocate() 方法分配缓冲区,将缓冲区建立在 JVM 的内存中

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

 */

public class TestBuffer {

    

    @Test

    public void test3(){

        //分配直接缓冲区

        ByteBuffer buf = ByteBuffer.allocateDirect(1024);

        

        System.out.println(buf.isDirect());

    }

    

    @Test

    public void test2(){

        String str = "abcde";

        

        ByteBuffer buf = ByteBuffer.allocate(1024);

        

        buf.put(str.getBytes());

        

        buf.flip();

        

        byte[] dst = new byte[buf.limit()];

        buf.get(dst, 0, 2);

        System.out.println(new String(dst, 0, 2));

        System.out.println(buf.position());

        

        //mark() : 标记

        buf.mark();

        

        buf.get(dst, 2, 2);

        System.out.println(new String(dst, 2, 2));

        System.out.println(buf.position());

        

        //reset() : 恢复到 mark 的位置

        buf.reset();

        System.out.println(buf.position());

        

        //判断缓冲区中是否还有剩余数据

        if(buf.hasRemaining()){

            

            //获取缓冲区中可以操作的数量

            System.out.println(buf.remaining());

        }

    }

    

    @Test

    public void test1(){

        String str = "abcde";

        

        //1. 分配一个指定大小的缓冲区

        ByteBuffer buf = ByteBuffer.allocate(1024);

        

        System.out.println("-----------------allocate()----------------");

        System.out.println(buf.position());

        System.out.println(buf.limit());

        System.out.println(buf.capacity());

        

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

        buf.put(str.getBytes());

        

        System.out.println("-----------------put()----------------");

        System.out.println(buf.position());

        System.out.println(buf.limit());

        System.out.println(buf.capacity());

        

        //3. 切换读取数据模式

        buf.flip();

        

        System.out.println("-----------------flip()----------------");

        System.out.println(buf.position());

        System.out.println(buf.limit());

        System.out.println(buf.capacity());

        

        //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());

        System.out.println(buf.limit());

        System.out.println(buf.capacity());

        

        //5. rewind() : 可重复读

        buf.rewind();

        

        System.out.println("-----------------rewind()----------------");

        System.out.println(buf.position());

        System.out.println(buf.limit());

        System.out.println(buf.capacity());

        

        //6. clear() : 清空缓冲区. 但是缓冲区中的数据依然存在,但是处于“被遗忘”状态

        buf.clear();

        

        System.out.println("-----------------clear()----------------");

        System.out.println(buf.position());

        System.out.println(buf.limit());

        System.out.println(buf.capacity());

        

        System.out.println((char)buf.get());

        

    }

}

几个重要属性的演示图

            
    非直接缓冲区,

            需要在操作系统内部和jvm内存中执行一个拷贝和黏贴的过程
            

    直接缓冲区,
        不需要在内存中缓冲,直接在物理硬盘中开辟空间    

        

三、通道(Channel)的原理与获取

 

计算机由原来的DMA(Direct Memory Access,直接内存存取)的方式改成了通道单独决策和io接口之间进行通信

 

①6. NIO_通道的数据传输与内存映射文件

②7. NIO_分散读取与聚集写入

③8. NIO_字符集 Charset

/*

 * 一、通道(Channel):用于源节点与目标节点的连接。在 Java NIO 中负责缓冲区中数据的传输。Channel 本身不存储数据,因此需要配合缓冲区进行传输。

 * 

 * 二、通道的主要实现类

 *     java.nio.channels.Channel 接口:

 *         |--FileChannel

 *         |--SocketChannel

 *         |--ServerSocketChannel

 *         |--DatagramChannel

 * 

 * 三、获取通道

 * 1. Java 针对支持通道的类提供了 getChannel() 方法

 *         本地 IO:

 *         FileInputStream/FileOutputStream

 *         RandomAccessFile

 * 

 *         网络IO:

 *         Socket

 *         ServerSocket

 *         DatagramSocket

 *         

 * 2. 在 JDK 1.7 中的 NIO.2 针对各个通道提供了静态方法 open()

 * 3. 在 JDK 1.7 中的 NIO.2 的 Files 工具类的 newByteChannel()

 * 

 * 四、通道之间的数据传输

 * transferFrom()

 * transferTo()

 * 

 * 五、分散(Scatter)与聚集(Gather)

 * 分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中

 * 聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中

 * 

 * 六、字符集:Charset

 * 编码:字符串 -> 字节数组

 * 解码:字节数组  -> 字符串

 * 

 */

public class TestChannel {

    

    //字符集

    @Test

    public void test6() throws IOException{

        Charset cs1 = Charset.forName("GBK");

        

        //获取编码器

        CharsetEncoder ce = cs1.newEncoder();

        

        //获取解码器

        CharsetDecoder cd = cs1.newDecoder();

        

        CharBuffer cBuf = CharBuffer.allocate(1024);

        cBuf.put("尚硅谷威武!");

        cBuf.flip();

        

        //编码

        ByteBuffer bBuf = ce.encode(cBuf);

        

        for (int i = 0; i < 12; i++) {

            System.out.println(bBuf.get());

        }

        

        //解码

        bBuf.flip();

        CharBuffer cBuf2 = cd.decode(bBuf);

        System.out.println(cBuf2.toString());

        

        System.out.println("------------------------------------------------------");

        

        Charset cs2 = Charset.forName("GBK");

        bBuf.flip();

        CharBuffer cBuf3 = cs2.decode(bBuf);

        System.out.println(cBuf3.toString());

    }

    

    @Test

    public void test5(){

        Map<String, Charset> map = Charset.availableCharsets();

        

        Set<Entry<String, Charset>> set = map.entrySet();

        

        for (Entry<String, Charset> entry : set) {

            System.out.println(entry.getKey() + "=" + entry.getValue());

        }

    }

    

    //分散和聚集

    @Test

    public void test4() throws IOException{

        RandomAccessFile raf1 = new RandomAccessFile("1.txt", "rw");

        

        //1. 获取通道

        FileChannel channel1 = raf1.getChannel();

        

        //2. 分配指定大小的缓冲区

        ByteBuffer buf1 = ByteBuffer.allocate(100);

        ByteBuffer buf2 = ByteBuffer.allocate(1024);

        

        //3. 分散读取

        ByteBuffer[] bufs = {buf1, buf2};

        channel1.read(bufs);

        

        for (ByteBuffer byteBuffer : bufs) {

            byteBuffer.flip();

        }

        

        System.out.println(new String(bufs[0].array(), 0, bufs[0].limit()));

        System.out.println("-----------------");

        System.out.println(new String(bufs[1].array(), 0, bufs[1].limit()));

        

        //4. 聚集写入

        RandomAccessFile raf2 = new RandomAccessFile("2.txt", "rw");

        FileChannel channel2 = raf2.getChannel();

        

        channel2.write(bufs);

    }

    

    //通道之间的数据传输(直接缓冲区)

    @Test

    public void test3() throws IOException{

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

        FileChannel outChannel = FileChannel.open(Paths.get("d:/2.mkv"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);

        

//        inChannel.transferTo(0, inChannel.size(), outChannel);

        outChannel.transferFrom(inChannel, 0, inChannel.size());

        

        inChannel.close();

        outChannel.close();

    }

    

    //使用直接缓冲区完成文件的复制(内存映射文件)

    @Test

    public void test2() throws IOException{//2127-1902-1777

        long start = System.currentTimeMillis();

        

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

        FileChannel outChannel = FileChannel.open(Paths.get("d:/2.mkv"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);

        

        //内存映射文件

        MappedByteBuffer inMappedBuf = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());

        MappedByteBuffer outMappedBuf = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());

        

        //直接对缓冲区进行数据的读写操作

        byte[] dst = new byte[inMappedBuf.limit()];

        inMappedBuf.get(dst);

        outMappedBuf.put(dst);

        

        inChannel.close();

        outChannel.close();

        

        long end = System.currentTimeMillis();

        System.out.println("耗费时间为:" + (end - start));

    }

    

    //利用通道完成文件的复制(非直接缓冲区)

    @Test

    public void test1(){//10874-10953

        long start = System.currentTimeMillis();

        

        FileInputStream fis = null;

        FileOutputStream fos = null;

        //①获取通道

        FileChannel inChannel = null;

        FileChannel outChannel = null;

        try {

            fis = new FileInputStream("d:/1.mkv");

            fos = new FileOutputStream("d:/2.mkv");

            

            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(outChannel != null){

                try {

                    outChannel.close();

                } catch (IOException e) {

                    e.printStackTrace();

                }

            }

            

            if(inChannel != null){

                try {

                    inChannel.close();

                } catch (IOException e) {

                    e.printStackTrace();

                }

            }

            

            if(fos != null){

                try {

                    fos.close();

                } catch (IOException e) {

                    e.printStackTrace();

                }

            }

            

            if(fis != null){

                try {

                    fis.close();

                } catch (IOException e) {

                    e.printStackTrace();

                }

            }

        }

        

        long end = System.currentTimeMillis();

        System.out.println("耗费时间为:" + (end - start));

        

    }

}

四、阻塞与非阻塞

阻塞模式示例图:

客户端进行数据传输时,服务器端都会分配一个线程与其相对应

非阻塞模式示例图:

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿的十万个为什么

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值