Java持久化之 -- 傲娇的NIO

NIO

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

   所在包:java.nio

执行流程:

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

 

 

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

 

 

三个组件:

1. channel   通道

2. Buffer   缓冲区

3. Selector   选择器

NIOIO 的区别

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

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

3. 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();
        }

    }
}
 

 

posted @ 2018-07-06 09:05  房上的猫  阅读(829)  评论(1编辑  收藏  举报