Java持久化之 -- 傲娇的NIO

NIO:

Jdk 1.4+ New IO面向通道和缓冲区

所在包:java.nio

执行流程:

数据总数由通道写入到buffer, 或者是从buffer写入通道

图片描述

完全替换IO(面向流 单向的)

图片描述

三个组件:

  1. channel通道

  2. Buffer缓冲区

  3. Selector选择器

NIO和IO 的区别

1.传统的IO面向流 ,NIO面向缓冲区

2.传统的IO是阻塞IO,NIO是非阻塞IO(可并行,,可占位)

  1. NOI增加了新功能

①由选择器

②可以使用正则表达式

③支持内存映射(计算快,效率快)

④支持文件锁

一:buffer 缓冲区

读写两种模式

本质上就是一个数据集数组?集合?
本质是一个可以写入数据,并且从中读取数据的内存!!!存储的是相同数据类型的数据集

三个重要的值:

  1. Position:写入或者读取的数据的当前指针

  2. Limit:有多少数据可以写或者可以读

  3. Capacity:缓冲区的最大容量

在写(write)模式的情况下

limit和capacity值一致

Position最大 值{下标(0开始)}是capacity-1

写到哪值是什么从0开始

指针的值是真实值+1 -->将要写的位置(最大到capacity值)

xxxBuffer buffer = xxxBuffer.allocate(最大容量);

Buffer.put(xx);写入数据

在读(read)模式的情况下

Position读到那值值是几,,但从0开始

Limit的值是position写模式的值(可读数据)

重设缓冲区切换到读模式

Buffer.flip();

图片描述

小Tip:

package com.fsdm.nio.buffer;

import java.nio.IntBuffer;

/**
 * @author 房上的猫
 * @create 2018-07-03 17:11
 * @博客地址: https://www.cnblogs.com/lsy131479/
 * <p>
 * NIO 初探缓冲区
 **/

public class BufferTest {
    public static void main(String[] args) {
        //创建缓冲区实例
        IntBuffer buffer = IntBuffer.allocate(10);
        System.out.println( "\n\n=====================写操作====================\n\n");
        //监控各值
        System.out.println( "*************** 写模式初始值 ***************");
        System.out.println( "capacity=== » " +buffer.capacity());
        System.out.println( "position=== » " +buffer.position());
        System.out.println( "limit===» "+buffer. limit());
        //写入数据
        buffer.put(new int[]{1,1,1,2});
        //监控各值
        System.out.println( "*************** 写入值后 ***************");
        System.out.println( "capacity=== » " +buffer.capacity());
        System.out.println( "position=== » " +buffer.position());
        System.out.println( "limit===» "+buffer. limit());
        //重设缓冲区  切换到读模式
        buffer.flip();
        System.out.println( "\n\n====================读操作=====================\n\n");
        //监控各值
        System.out.println( "*************** 读模式初始值 ***************");
        System.out.println( "capacity=== » " +buffer.capacity());
        System.out.println( "position=== » " +buffer.position());
        System.out.println( "limit===» "+buffer. limit());
        //简单的读操作
        while (buffer.hasRemaining()){
            System.out.println(buffer.get());
            //监控各值
            System.out.println( "*************** 读取中 ***************");
            System.out.println( "capacity=== » " +buffer.capacity());
            System.out.println( "position=== » " +buffer.position());
            System.out.println( "limit===» "+buffer. limit());
        }

    }
}

二:channel 管道/通道

作用:

1.基于buffer(缓冲区)对数据进行读写

2.管道是双向的,流是单向的

3.可以异步的读写

常用实现类:

网络传输:
UDP:面向非连接,无脑流,效率高,性能好,非安全

TCP:面向连接,效率低,性能差,安全

  1. FileChannel:从文件中读写数据

  2. DataGrarmChannel:通过UDP来读写网络中数据

  3. SocketChannel:通过TCP读写网络中的数据

  4. ServerSocketChannel:可以监听新来的TCP连接,每进来一个,都会创建一个新的SocketChannel

小Tip:

package com.fsdm.nio.channel;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.FileChannel;

/**
 * @author 房上的猫
 * @create 2018-07-05 14:09
 * @博客地址: https://www.cnblogs.com/lsy131479/
 * <p>
 * 通过管道向文件中读写数据
 **/

public class ChannelDemo {
    public static void main(String[] args) {
        //准备数据
        String[] strs = {"haha","hehe","heihei"};
        //写入  文件   输出流
        FileOutputStream fos=null;
        //准备管道
        FileChannel channel = null;
        try {
             fos = new FileOutputStream("f:/a.txt");
             //获取管道数据
            channel = fos.getChannel();
            //准备缓冲区
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            //将事先准备好的数据  写入缓冲区
            for (String str:strs) {
                buffer.put(str.getBytes());
                buffer.put("\n".getBytes());
            }
            //将缓存区切换到读模式
            buffer.flip();
            //将缓冲区数据读取出来并写入磁盘  真正的写
            channel.write(buffer);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //回收资源
            try {
                channel.close();
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

练习实例:(利用管道将a文件内容复制到b文件):

package com.fsdm.nio.channel;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * @author 房上的猫
 * @create 2018-07-05 15:02
 * @博客地址: https://www.cnblogs.com/lsy131479/
 * <p>
 * a.txt   -->  b.txt
 **/

public class ChannelBuffer {
    public static void main(String[] args) {
        //准备起始文件与终止文件
        File inFile = new File("f:/a.txt");
        File outFile = new File("f:/b.txt");
        //准备输入输出流
        FileInputStream fis = null;
        FileOutputStream fos = null;
        //准备双向管道
        FileChannel inChannel = null;
        FileChannel outChannel = null;
        try {
            //实例化各对象
            fis = new FileInputStream(inFile);
            fos = new FileOutputStream(outFile);
            inChannel = fis.getChannel();
            outChannel = fos.getChannel();

            //准备缓存区  (作为中转站)
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int num = 0;

            //写入到缓冲区
            while ((num=inChannel.read(buffer))!=-1){
                //转换缓冲区模式
                buffer.flip();
                //读取缓冲区数据并写入到磁盘
                outChannel.write(buffer);
                //清空缓冲区 方便下次读写
                buffer.clear();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                inChannel.close();
                fis.close();
                outChannel.close();
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

三:selector 选择器

待后期单独总结

高并发:NIO,线程

Java 运行时数据区

运行时都会被创建共享数据:
堆heap

方法区method area

私有数据:
虚拟机栈vm stack

本地方法栈native method stack
程序计数器

Xms:初始化容量

Xmx:最大容量

内存映射:

就是把文件映射到电脑中的内存中,通过操作内存从而打到操作文件的目的内存中操作速度是最快的

Java 中读取文件的几种方式:

  1. RandomAceessFile随机读取,速度最慢

  2. FileInputStream流的方式读取

  3. BufferReader缓存的方式读取

  4. MappedByteBuffer内存映射,速度最快

内存映射的三种模式:MapMode

  1. READ_ONLY:对缓冲区的内存只读

  2. READ_WRITE:对缓冲区的内存读写

  3. PRIVATE:只会对缓冲区的内存进行修改,不会影响到真实的文件
    通常适用于数据的读取,一般不会进行对数据的写入

内存映射读取文件与普通读取文件 效率对比:

package com.fsdm.nio;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/**
 * @author 房上的猫
 * @create 2018-07-05 18:00
 * @博客地址: https://www.cnblogs.com/lsy131479/
 * <p>
 * 内存映射
 **/

public class MapperDemo {
    public static void main(String[] args) {
        FileChannel channel = null;
        RandomAccessFile file = null;
        try {
            file = new RandomAccessFile("e:/struts-2.3.31-lib.zip","rw");
            //获取通道
            channel = file.getChannel();
            //创建内存映射对象
            MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY,0,channel.size());
            byte[] bytes = new byte[1024];
            //获取文件大小
            long length = file.length();
            long begin = System.currentTimeMillis();
            ByteBuffer buffer2 = ByteBuffer.allocate(1024);
            for (int i=0;i<length;i+=1024){
                if (length-i>1024){
                    buffer2=buffer.get(bytes);
                }else{
                    buffer2=buffer.get(new byte[(int)(length-i)]);
                }
                buffer2.flip();
                buffer2.clear();
            }
            long end = System.currentTimeMillis();
            System.out.println(end-begin);
            System.out.println("================");
            begin = System.currentTimeMillis();
            //普通读取缓冲区
            ByteBuffer buffer1 = ByteBuffer.allocate(1024);
            while (channel.read(buffer1)!=-1){
                buffer1.flip();
                buffer.clear();
            }
             end = System.currentTimeMillis();
            System.out.println(end-begin);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

文件锁:

FileLock:基于FlieChannel对文件提供锁的功能

共享锁:

共享读的操作读可以有多个,但是只能有一个人在写适合读取数据目的:是为了防止其他线程拿到独占锁

独占锁:

只能有一个读或写读写不能同时适合写数据

Lock():

阻塞无参默认是独占锁有参的可设置锁状态

TyLock():

非阻塞

小Tip 玩玩?:

package com.fsdm.nio.lock;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;

/**
 * @author 房上的猫
 * @create 2018-07-05 18:15
 * @博客地址: https://www.cnblogs.com/lsy131479/
 * <p>
 * 锁
 **/

public class LockDemo implements Runnable {
    static RandomAccessFile file = null;
    static FileChannel channel = null;
    static FileLock lock = null;

    public static void main(String[] args) {
        Thread thread = null;
        try {

            // lock = channel.lock(0L, Long.MAX_VALUE, true);
        } catch (Exception e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 10; i++) {
            try {
                file = new RandomAccessFile("f:/a.txt", "rw");
                channel = file.getChannel();
                if (i==0){
                    lock = channel.lock();
                   // lock = channel.lock(0L, Long.MAX_VALUE, true);
                    buffer.put("xx".getBytes());

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

            LockDemo lockDemo = new LockDemo();
            thread = new Thread(lockDemo, i+":");
            thread.start();
        }
        try {
            System.out.println(Thread.currentThread().getName()+(char)( channel.write(buffer)));
        } catch (IOException e) {
            e.printStackTrace();
        }
        ;
    }
   static   ByteBuffer buffer = ByteBuffer.allocate(1024);
    @Override
    public void run() {
        try {
            buffer =ByteBuffer.allocate(1024);
            buffer.put("xx".getBytes());
            System.out.println(Thread.currentThread().getName()+(char)( channel.write(buffer)));;
            //System.out.println(Thread.currentThread().getName()+(char)( channel.read(buffer)));;
        } catch (Exception e){
            e.printStackTrace();
        }

    }
}