初识 NIO, 你了解io 和nio吗? 了解直接缓冲区和非直接缓冲区的区别吗?_兮家小二的博客

CSDN博客 · · 807 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

一、nio 是什么?

1、Java NIO(New IO)是一个可以替代标准Java IO API的IO API(从Java 1.4开始)
2、Java NIO提供了与标准IO不同的IO工作方式。
3、nio 主要面向于网络编程

二、nio 和 io 的区别?

1、IO基于字节流和字符流进行操作的
2、NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。
3、NIO引入了选择器的概念,选择器用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个的线程可以监听多个数据通道。
在这里插入图片描述
io 流实现过程
数据传递是单向的,写数据只管把数据往文件丢,读也是同理
并且 io 基本都是非直接缓冲区传递(速度慢)
在这里插入图片描述
nio 实现过程
先把数据放到缓冲区,在根据缓冲区的大小来回读取,直到数据传递完成
在这里插入图片描述

三、NIO 与传统 IO的优势?

------在老的IO包中,serverSocket和socket都是阻塞式的,因此一旦有大规模的并发行为,而每一个访问都会开启一个新线程。这时会有大规模的线程上下文切换操作(因为都在等待,所以资源全都被已有的线程吃掉了),这时无论是等待的线程还是正在处理的线程,响应率都会下降,并且会影响新的线程。

------而NIO包中的serverSocket和socket就不是这样,只要注册到一个selector中,当有数据放入通道的时候,selector就会得知哪些channel就绪,这时就可以做响应的处理,这样服务端只有一个线程就可以处理大部分情况(当然有些持续性操作,比如上传下载一个大文件,用NIO的方式不会比IO好)。

四、NIO 直接缓冲区和非直接缓冲区的区别?

1、非直接缓冲区是直接通过拷贝的形式传递的,如从磁盘读取文件到物流空间,然后拷贝到jvm,在读取数据
优点:安全
缺点:速度慢,不是一般的慢, 直接缓冲区比非直接缓冲区大约快5倍

在这里插入图片描述
2、直接缓冲区是通过物流内存映射文件直接传递的
优点:速度快
缺点:不安全,占CPU(传递特别大的文件,如几个G的,特别占cpu,严重情况可能会导致电脑直接卡死)
在这里插入图片描述

五、java 使用NIO 读写文件操作

1、直接缓冲区
2、非直接缓冲区
把1.pm4 拷贝到 2.mp4
在这里插入图片描述


package com.itmayiedu;

import org.junit.Test;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;


@SuppressWarnings("ALL")
public class Test003 {

	/**
	 * 直接缓冲区
	 * @throws IOException
	 */
	@Test
	public void test002() throws IOException {
		long statTime=System.currentTimeMillis();
		//创建管道
		FileChannel  inChannel=	FileChannel.open(Paths.get("f://1.mp4"), StandardOpenOption.READ);
		FileChannel  outChannel=FileChannel.open(Paths.get("f://2.mp4"), StandardOpenOption.READ,StandardOpenOption.WRITE, StandardOpenOption.CREATE);
	    //定义映射文件
		MappedByteBuffer inMappedByte = inChannel.map(MapMode.READ_ONLY,0, inChannel.size());
		MappedByteBuffer outMappedByte = outChannel.map(MapMode.READ_WRITE,0, inChannel.size());
		//直接对缓冲区操作
		byte[] dsf=new byte[inMappedByte.limit()];
		inMappedByte.get(dsf);
		outMappedByte.put(dsf);
		inChannel.close();
		outChannel.close();
		long endTime=System.currentTimeMillis();
		System.out.println("操作直接缓冲区耗时时间:"+(endTime-statTime));
	}

	/**
	 * 非直接缓冲区 读写操作
	 * @throws IOException
	 */
	@Test
	public void test001() throws IOException {
		long statTime=System.currentTimeMillis();
		// 读入流
		FileInputStream fst = new FileInputStream("f://1.mp4");
		// 写入流
		FileOutputStream fos = new FileOutputStream("f://2.mp4");
		// 创建通道
		FileChannel inChannel = fst.getChannel();
		FileChannel outChannel = fos.getChannel();
		// 分配指定大小缓冲区
		ByteBuffer buf = ByteBuffer.allocate(1024);
		while (inChannel.read(buf) != -1) {
			// 开启读取模式
			buf.flip();
			// 将数据写入到通道中
			outChannel.write(buf);
			buf.clear();
		}
		// 关闭通道 、关闭连接
		inChannel.close();
		outChannel.close();
		fos.close();
		fst.close();
		long endTime=System.currentTimeMillis();
		System.out.println("操作非直接缓冲区耗时时间:"+(endTime-statTime));
	}
}

测试结果

1、直接缓冲区
在这里插入图片描述
2、非直接缓冲区
操作非直接缓冲区耗时时间:2113

五、分散读取与聚集写入

首先先介绍一下RandomAccessFile || RandomAccessFile

//创建随机存储文件流,文件属性由参数File对象指定
RandomAccessFile(File file ,  String mode)

//创建随机存储文件流,文件名由参数name指定
RandomAccessFile(String name ,  String mode)

这两个构造方法均涉及到一个String类型的参数mode,它决定随机存储文件流的操作模式,其中mode值及对应的含义如下:

“r”:  以只读的方式打开,调用该对象的任何write(写)方法都会导致IOException异常
“rw”: 以读、写方式打开,支持文件的读取或写入。若文件不存在,则创建之。
“rws”:以读、写方式打开,与“rw”不同的是,还要对文件内容的每次更新都同步更新到潜在的存储设备中去。这里的“s”表示synchronous(同步)的意思
“rwd”:以读、写方式打开,与“rw”不同的是,还要对文件内容的每次更新都同步更新到潜在的存储设备中去。使用“rwd”模式仅要求将文件的内容更新到存储设备中,而使用“rws”模式除了更新文件的内容,还要更新文件的元数据(metadata),因此至少要求1次低级别的I/O操作

分散读取与聚集写入读的文件1.txt 数据
在这里插入图片描述

代码如下


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

@SuppressWarnings("ALL")
public class Test004 {
	public static void main(String[] args) throws IOException {

		try {
			//分散读取
			RandomAccessFile randomAccessFile = new RandomAccessFile("f://1.txt","rw");
			//获取channel
			FileChannel fileChannel = randomAccessFile.getChannel();
			//分散读取,就是使用多个buffer来装数据,可继续增加,值为名称缓冲区大小
			ByteBuffer by1 = ByteBuffer.allocate(20);
			ByteBuffer by2 = ByteBuffer.allocate(50);
			ByteBuffer[] byteBuffers = {by1,by2};
			//分散聚合
			RandomAccessFile randomAccessFile1 = new RandomAccessFile("f://2.txt","rw");
			FileChannel fileChannel1 = randomAccessFile1.getChannel();
			while (fileChannel.read(byteBuffers) != -1){
				by1.flip();
				by2.flip();
				System.out.println("------------------打印数据by1-------------------");
				System.out.println(new String(byteBuffers[0].array(), 0, byteBuffers[0].limit(),"utf-8"));
				System.out.println("------------------打印数据by2-------------------");
				System.out.println(new String(byteBuffers[1].array(), 0, byteBuffers[1].limit(),"utf-8"));
				//聚合,同上
				fileChannel1.write(byteBuffers);
				by1.clear();
				by2.clear();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

效果
在这里插入图片描述
而最后聚合的数据是完全正确的
在这里插入图片描述
到此就结束了

807 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传