原文链接http://zhhll.icu/2020/05/18/java%E5%9F%BA%E7%A1%80/IO/NIO%E5%9F%BA%E6%9C%AC%E6%93%8D%E4%BD%9C/

NIO

同步非阻塞

阻塞与非阻塞的区别:

  • 阻塞时,在调用结果返回时,当前线程会被挂起,并在得到结果之后返回
  • 非阻塞时,如不能立即得到结果,该调用不会阻塞当前线程,调用者需要定时轮询查看处理状态

Channel(通道)和Buffer(缓冲区)

与普通IO的不同和关系

  • NIO以块的方式处理数据,但是IO是以最基础的字节流的形式去写入和读出的

  • NIO不再是和IO一样用OutputStream和InputStream输入流的形式来进行处理数据的,但是又是基于这种流的方式,采用了通道和缓冲区的形式进行处理

  • NIO的通道是可以双向的,IO的流只能是单向的

  • NIO的缓冲区(字节数组)还可以进行分片,可以建立只读缓冲区、直接缓冲区和间接缓冲区,只读缓冲区就是只可以读,直接缓冲区是为了加快I/O速度,以一种特殊的方式分配其内存的缓冲区

  • NIO采用的是多路复用的IO模型,BIO用的是阻塞的IO模型

通道的概念

通道是对原I/O包中的流的模拟。到任何目的地的所有数据都必须通过一个Channel对象(通道)。一个Buffer实质上就是一个容器对象。发送给一个通道的所有对象都必须首先放到缓冲区中;从通道中读取的任何数据都要读到缓冲区中

缓冲区的概念

  • Buffer是一个对象,它包含一些要写入或者刚读出的数据。在NIO中加入Buffer对象,在流式IO中,将数据直接写入或者读到Stream对象中

  • 在NIO库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的。在写入数据时,它是写入到缓冲区的。任何时候访问NIO中的数据,都需要将它放到缓冲区中

  • 缓冲区实质上是一个数组。通常它是一个字节数组,但是也可以使用其他种类的数组。但是一个缓冲区不仅仅是一个数组,缓冲区提供了对数据的结构化访问,而且还可以跟踪系统的读/写进程

ByteBuffer
CharBuffer
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer

选择器

Selector是多路复用器,用于同时检测多个通道的事件以实现异步I/O。

通过一个选择器来同时对多个套接字通道进行监听,当套接字通道有可用的事件的时候,通道改为可用状态,选择器就可以实现可用的状态。

工作原理

客户端-----》Channel-----》Selector------》keys--状态改变---》server

Buffer 缓冲区

Channel 通道

Selector 选择器

Server端创建ServerSocketChannel 有一个Selector多路复用器 轮询所有注册的通道,根据通道状态,执行相关操作

  • Connect 连接状态
  • Accept 阻塞状态
  • Read 可读状态
  • Write 可写状态

Client端创建SocketChannel 注册到Server端的Selector

buffer

  • capacity 缓冲区数组的总长度
  • position 下一个要操作的数据元素的位置
  • limit 缓冲区数组中不可操作的下一个元素的位置,limit<=capacity
  • mark 用于记录当前position的前一个位置或者默认是0
  • clear/flip/rewind等都是操作limit和position的值来实现重复读写的

ByteBuffer

有且仅有ByteBuffer(字节缓冲区)可以直接与通道交互。

public static void main(String[] args) {
//生成FileChannel文件通道 FileChannel的操作--> 操作ByteBuffer用于读写,并独占式访问和锁定文件区域 // 写入文件
try(FileChannel fileChannel = new FileOutputStream(FILE).getChannel()){
fileChannel.write(ByteBuffer.wrap("test".getBytes()));
} catch (IOException e){
throw new RuntimeException("写入文件失败",e);
}
// 在文件结尾写入
try(FileChannel fileChannel = new RandomAccessFile(FILE,"rw").getChannel()){
fileChannel.position(fileChannel.size());//移至文件结尾
fileChannel.write(ByteBuffer.wrap("some".getBytes()));
} catch (IOException e){
throw new RuntimeException("写入文件结尾失败",e);
} try(FileChannel fileChannel = new FileInputStream(FILE).getChannel();
FileChannel out = new FileOutputStream("C:\\Users\\sinosoft\\Desktop\\copy.txt").getChannel()
){
// 读取操作,需要调用allocate显示分配ByteBuffer
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// read之后将数据放入缓冲区
while (fileChannel.read(byteBuffer) != -1){
byteBuffer.flip(); // 准备写入
out.write(byteBuffer);
byteBuffer.clear(); // 清空缓存区
}
} catch (IOException e){
throw new RuntimeException("读取文件失败",e);
}
}

方法说明

rewind()方法是将position设置为缓冲区的开始位置

get()和put()都会修改position

get(int)和put(int)都不会修改position

mark()设置mark为当前position

flip()将写模式切换为读模式

public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}

内存映射文件

内存映射文件可以创建和修改那些因为太大而无法放入内存的文件。

RandomAccessFile tdat = new RandomAccessFile("test.dat", "rw");
MappedByteBuffer out = tdat.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, length); 或者
FileChannel fc = new FileInputStream(new File("temp.tmp")).getChannel();
IntBuffer ib = fc.map(FileChannel.MapMode.READ_ONLY,0, fc.size()).asIntBuffer();

映射文件访问比标准IO性能高很多

文件锁定

文件锁定可同步访问,文件锁对其他操作系统进程可见,因为java文件锁直接映射到本机操作系统锁定工具。

public class FileLockTest {
private static final String FILE = "C:\\Users\\sinosoft\\Desktop\\剩余工作副本.txt";
public static void main(String[] args) throws IOException, InterruptedException {
FileChannel fileChannel = new FileOutputStream(FILE).getChannel(); // 文件锁
FileLock fileLock = fileChannel.tryLock();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
FileChannel fileChannel = null;
try {
fileChannel = new FileOutputStream(FILE).getChannel();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
byteBuffer.put("aqws".getBytes());
try {
System.out.println("线程准备写");
fileChannel.write(byteBuffer);
System.out.println("线程写完");
} catch (IOException e) {
e.printStackTrace();
}
}
});
thread.start();
if(fileLock != null){
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
byteBuffer.put("aqwqdqdhwfwihfejfhi".getBytes());
System.out.println("主线程睡眠");
Thread.sleep(10000);
// 会报错 java.nio.channels.NonWritableChannelException
// fileChannel.read(byteBuffer);
System.out.println("主线程准备写");
fileChannel.write(byteBuffer);
fileLock.release();
}
}
} 主线程睡眠
线程准备写
java.io.IOException: 另一个程序已锁定文件的一部分,进程无法访问。
at sun.nio.ch.FileDispatcherImpl.write0(Native Method)
at sun.nio.ch.FileDispatcherImpl.write(FileDispatcherImpl.java:75)
at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93)
at sun.nio.ch.IOUtil.write(IOUtil.java:65)
at sun.nio.ch.FileChannelImpl.write(FileChannelImpl.java:211)
at com.zhanghe.study.io.nio.FileLockTest$1.run(FileLockTest.java:35)
at java.lang.Thread.run(Thread.java:745)
主线程准备写

通过调用FileChannel上的tryLock或lock,可以获得整个文件的FileLock(SocketChannel、DatagramChannel和ServerSocketChannel不需要锁定,因为本质上就是单线程实体)

tryLock()是非阻塞的,试图获取锁,若不能获取,只是从方法调用返回

lock()会阻塞,直到获得锁,或者调用lock()的线程中断,或者调用lock()方法的通道关闭。

使用FileLock.release()释放锁

// 锁定文件的一部分,锁住size-position区域。第三个参数指定是否共享此锁
tryLock(long position, long size, boolean shared)

由于本身的博客百度没有收录,博客地址http://zhhll.icu

NIO基础操作的更多相关文章

  1. python基础操作以及hdfs操作

    目录 前言 基础操作 hdfs操作 总结 一.前言        作为一个全栈工程师,必须要熟练掌握各种语言...HelloWorld.最近就被"逼着"走向了python开发之路, ...

  2. MYSQL基础操作

    MYSQL基础操作 [TOC] 1.基本定义 1.1.关系型数据库系统 关系型数据库系统是建立在关系模型上的数据库系统 什么是关系模型呢? 1.数据结构可以规定,同类数据结构一致,就是一个二维的表格 ...

  3. 【Learning Python】【第二章】Python基础类型和基础操作

    基础类型: 整型: py 3.0解决了整数溢出的问题,意味着整型不必考虑32位,64位,有无符号等问题,你写一个1亿亿亿,就是1亿亿亿,不会溢出 a = 10 ** 240 print(a) 执行以上 ...

  4. Emacs学习心得之 基础操作

    作者:枫雪庭 出处:http://www.cnblogs.com/FengXueTing-px/ 欢迎转载 Emacs学习心得之 基础操作 1.前言与学习计划2.Emacs基础操作 一. 前言与学习计 ...

  5. Git基础操作

    配置秘钥 1.检查本机有没有秘钥 检查~/.ssh看看是否有名为d_rsa.pub和id_dsa.pub的2个文件. $ ~/.sshbash: /c/Users/lenovo/.ssh: Is a ...

  6. activiti基础操作

    package activitiTest; import java.io.InputStream; import java.util.List; import java.util.zip.ZipInp ...

  7. 《Genesis-3D开源游戏引擎-官方录制系列视频教程:基础操作篇》

    注:本系列教程仅针对引擎编辑器:v1.2.2及以下版本 G3D基础操作   第一课<G3D编辑器初探> G3D编辑器介绍,依托于一个复杂场景,讲解了场景视图及其基本操作,属性面板和工具栏的 ...

  8. MYSQL 基础操作

    1.MySQL基础操作 一:MySQL基础操作 1:MySQL表复制 复制表结构 + 复制表数据 create table t3 like t1; --创建一个和t1一样的表,用like(表结构也一样 ...

  9. php之文件基础操作

    在php中对文件的基础操作非常的简单,php提供的函数粗略的用了一遍. file_get_contents():可以获取文件的内容获取一个网络资源的内容,这是php给我封装的一个比较快捷的读取文件的内 ...

随机推荐

  1. sails框架结合mocha的测试环境搭建

    一.环境结构 1.首先最底层是操作系统 2.其次在操作系统之上是Node.js的运行环境,和Database 3.再之上就是sail和mocha框架 二.环境搭建 1.首先需要Node.js的运行环境 ...

  2. NodeService Ensure that Node.js is installed and can be found in one of the PATH directories

    今天发布NodeService到服务器,服务器环境是window server 2012 一直报错: [1] Ensure that Node.js is installed and can be f ...

  3. IDM 6.37.8 绿色特别版 (4月4日更新)

    Internet Download Manager,全球最流行的下载工具.Internet Download Manager (简称IDM) Windows 平台功能强大的多线程下载工具,国外非常受欢 ...

  4. 影评网站Alpha版本-测试与发布

    影评网站Alpha版本-测试与发布 项目发布地址: http://120.78.161.21:8080/zhiying/ (建议使用Chrome或火狐浏览器打开,其他浏览器可能加载失败 一.Alpha ...

  5. new一个对象时,会经历哪些步骤

    (1)创建一个对象:(2)将构造函数的作用域赋值给新对象(因此this就指向了这个新对象):(3)执行构造函数中的代码(为这个新对象添加属性):(4)返回新对象

  6. C#软件性能优化

    C#软件性能优化 1.    性能 衡量一个软件系统性能的常见指标有:响应时间.负载.资源使用率.并发数.在软件中有具体的提高性能需求时,我们需分析该系统性能的影响由哪些因素组成,再针对各部分进行性能 ...

  7. 写给OIer们的一些话(修订版)

    我是一个高二的OIer,离我正式退役的日子已经不超过一年了.在这个时期,与其写一些回忆性的文字,不如跳出"自我"的范畴,以一种比较全局的角度和大家一起分享一些我对OI的认知和看法. ...

  8. 【NOI2020】美食家(矩阵)

    Description 给定一张有向图,\(n\) 个顶点,\(m\) 条边.第 \(i\) 条边从 \(u_i\) 到 \(v_i\),走完该边的用时为 \(w_i\).每一个点有一个价值 \(c\ ...

  9. Python迭代器&生成器&装饰器

    1. 迭代器 1.1 可迭代对象(Iterator) 迭代器协议:某对象必须提供一个__next__()方法,执行方法要么返回迭代中的下一项,要么引起一个Stopiteration异常,以终止迭代(只 ...

  10. python写几个排序算法

    def maopao(data: list): """ 两两比较 :param data: :return: """ length = le ...