Java NIO

Java NIO

Java 新IO New IO, 简称NIO。
NIO和传统IO都用于进行输入/输出。
NIO采用内存映射文件的方式处理IO,面向缓冲区的。将文件或文件的一段区域映射到内存中,像访问内存一样访问文件。
传统IO是面向流的处理, NIO是面向块的处理。
NIO相关包:

  • java.nio : 与 Buffer相关的类
  • java.nio.channels : 与 Channel 和 Selector 相关的类
  • java.nio.charset : 字符集相关
    Channel(通道) 和 Buffer 是NIO中两个核心对象。

Buffer

Buffer可以理解成容器, 本质是一个数组。
主要用于装入数据, 然后输出数据。
Buffer是一个抽象类。
主要子类有 ByteBuffer, CharBuffer, ShortBuffer, IntBuffer, LongBuffer, FloatBuffer, DoubleBuffer .
allocate(int capacity) 创建一个容量为 capacity 的 XxxBuffer对象。
Buffer的三个重要概念: 容量capacity, 界限limit,位置position。
capacity: 缓冲区容量, Buffer最大的数据容量, 不能为负值,创建后不能修改。
limit: 第一个不能被读写的缓冲区位置索引。用于标记后面无数据。
position: 下一个可以读写的位置索引。类似于IO流中的记录指针。
mark: 可选标记, 类似IO流中的mark。
0 ≤ mark ≤ position ≤ limit ≤ capacity
开始时: position 为 0, limit 为 capacity。 put()写入一些数据后, position 往后移。
装入结束后调用flip(), limit设置为position位置, position设置为0。读写指针又回到了开始位置。
flip()为输出做好准备,
输出结束后,调用clear(), 不清空数据, 将 position设为0, limit设为capacity。为再次输入数据做准备。
Buffer常用方法:

  • capacity(): 获取capacity
  • hasRemaining(): 是否还有数据: position和limit之间
  • limit(): 获取limit, 还可重设limit
  • mark(): 设置mark, 0和 position之间
  • position(): 获取position, 还可重设position
  • remaining(): 当前和limit之间元素个数
  • reset(): 将 position设置到mark处
  • rewind(): position 设置为0, 取消mark。
  • put(): 放入数据
  • get(): 取出数据
    put()和get()支持两种:
    相对 Relative: 从position处读写数据, position更新
    绝对 Absolute: 根据索引读写, 不影响position值。
        CharBuffer buffer = CharBuffer.allocate(8);
        System.out.println("capacity:" + buffer.capacity());
        System.out.println("position :" + buffer.position());
        System.out.println("limit: " + buffer.limit());

        buffer.put('a');
        buffer.put('b');
        buffer.put('c');
        System.out.println("position :" + buffer.position());
        buffer.flip();
        System.out.println("position :" + buffer.position());
        System.out.println("limit: " + buffer.limit());

        System.out.println("first char :" + buffer.get());
        System.out.println("position :" + buffer.position());
        buffer.clear();
        System.out.println("position :" + buffer.position());
        System.out.println("limit: " + buffer.limit());

        System.out.println("third char :" + buffer.get(2));
        System.out.println("position :" + buffer.position());
//Output
capacity:8
position :0
limit: 8
position :3
position :0
limit: 3
first char :a
position :1
position :0
limit: 8
third char :c
position :0

ByteBuffer allocateDirect(int capacity) ByteBuffer 还支持创建 直接Buffer。
相比普通Buffer, direct byte buffer 创建成本高, 读取效率更高。

Channel

Channel类似于传统的流对象。
有下面区别:

  1. Channel可以将指定文件的部分或全部映射成Buffer。
  2. 不能直接访问Channel中数据, Channel只能与Buffer交互。读写时,程序与Buffer交互, Buffer再与Channel交互。

Channel接口的实现类有
FileChannel:文件管道
Pipe.SinkChannel, Pipe.SourceChannel: 线程之间通信的管道
ServerSocketChannel, SocketChannel: 支持TCP网络通信的管道
DatagramChannel: 支持UDP网络通信的管道。

Channel都不应该通过构造器直接创建, 应通过IO节点的 InputStream 和OutputStream的 getChannel() 返回。

Channel 常用方法:
map():将Channel对应的数据映射成Buffer。
read(): 从Buffer中读
write() : 写入Buffer。

        try(
                FileInputStream fis = new FileInputStream("abc.txt");
                FileChannel fileChannel = fis.getChannel();
                ){
            ByteBuffer byteBuffer = ByteBuffer.allocate(128);
            while (fileChannel.read(byteBuffer) != -1){
                //
                byteBuffer.flip();
                Charset charset = Charset.forName("utf-8");
                CharsetDecoder charsetDecoder = charset.newDecoder();
                CharBuffer cbuffer = charsetDecoder.decode(byteBuffer);
                System.out.println(cbuffer);

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

RandomAccessFile 也包含了getChannel() 方法

Charset

字符集, Charset是NIO中处理字节序列和字符序列(字符串)之间的转换。
availableCharsets 查看支持的字符集

 SortedMap<String, Charset> charMap = Charset.availableCharsets();
charMap.forEach(((s, charset) -> System.out.println(s + "---" + charset)));

forName设置Charset对象,
之后调用编码器和解码器, newDecoder(), newEncoder().
然后调用 encode()和 decode()讲ByteBuffer转换为CharBuffer。

        Charset cn = Charset.forName("utf-8");
        CharsetEncoder cnEncoder = cn.newEncoder();
        CharsetDecoder cnDecoder = cn.newDecoder();

        CharBuffer charBuffer = CharBuffer.allocate(8);
        charBuffer.put('张');
        charBuffer.put('三');
        charBuffer.flip();

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

        ByteBuffer byteBuffer = cnEncoder.encode(charBuffer);
        System.out.println(byteBuffer.limit());
        for(int i = 0; i < byteBuffer.limit(); i++){
            System.out.println(byteBuffer.get(i) + " ");
        }
        System.out.println(cnDecoder.decode(byteBuffer));
//Output
2
6
-27 
-68 
-96 
-28 
-72 
-119 
张三

文件锁

多个进程并发修改一个文件时, 使用文件锁来有效阻止。
NIO中, 通过 FileLock 来支持文件锁。
在 FileChannel 中提供 lock()/tryLock() 来获取文件锁对象。
lock(): 锁定文件时, 如果无法得到文件锁, 则一直阻塞
tryLock(): 锁定文件时, 得到文件锁则返回, 否则返回null, 不会阻塞。
如果锁定部分内容, 可以设置 开始 position, 和长度 size参数。
默认获取文件锁是 排它锁, shared参数 设置为true时,是共享锁,可以多个读取, 阻止其他进程获取排它锁。 为false时,设置为排它锁,锁住文件读写。
release()释放锁

try(
                FileChannel channel = new FileOutputStream("abc.txt").getChannel()
                )
        {
            FileLock fileLock = channel.tryLock();
            Thread.sleep(10000);
            fileLock.release();

        }
        catch (Exception e){
            e.printStackTrace();
        }
发布了120 篇原创文章 · 获赞 46 · 访问量 29万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览