一、IO和NIO区别
- IO是面向流的,阻塞的,单向的;
- NIO是面向缓冲的,非阻塞的,双向的;
二、缓冲区
2.1 基本概念
缓冲区(Buffter):在Java NIO中负责数据的存储,缓冲区就是数组,用于存储不同数据类型的数据。
JDK中8大基本类型,除了boolean没有提供对应的Buffter类外,其他7大基本类型都提供了对应的Buffter类,如:ByteBuffer、CharBuffer等等。
上述缓冲区的管理方式基本一致,都是通过allocate()获取缓冲区。
2.2 缓冲区中的四个核心属性
private int mark = -1 // 标记
private int position= 0 // 位置,表示缓冲区正在操作数据的位置
private int limit // 界限,表示缓冲区可以操作数据的大小
private int capacity // 容量,表示缓冲区中最大存储数据的容量
2.3缓冲区中的两个核心方法
put() // 存储数据到缓冲区
get() // 获取缓冲区中的数据
2.4 代码演示
import java.nio.ByteBuffer;
public class BufferTest {
public static void main(String[] args) {
test();
}
public static void test(){
// 1、分配一个指定大小的缓冲区
System.out.println("---------------allocate----------------");
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
System.out.println(byteBuffer.position()); // 0
System.out.println(byteBuffer.limit()); // 1024
System.out.println(byteBuffer.capacity()); // 1024
// 2、利用put()存入数据到缓冲区
System.out.println("---------------put----------------");
String str = "abcde";
byteBuffer.put(str.getBytes());
System.out.println(byteBuffer.position()); // 5
System.out.println(byteBuffer.limit()); // 1024
System.out.println(byteBuffer.capacity()); // 1024
// 3、切换到读模式 flip()
System.out.println("---------------flip----------------");
byteBuffer.flip();
System.out.println(byteBuffer.position()); // 0
System.out.println(byteBuffer.limit()); // 5
System.out.println(byteBuffer.capacity()); // 1024
// 4、利用get读取缓冲区中的数据
System.out.println("---------------read----------------");
byte[] bytes = new byte[byteBuffer.limit()];
byteBuffer.get(bytes);
System.out.println(new String(bytes)); // abcde
System.out.println(byteBuffer.position()); // 5
System.out.println(byteBuffer.limit()); // 5
System.out.println(byteBuffer.capacity()); // 1024
}
}
2.4 直接缓冲区和非直接缓冲区
- 非直接缓冲区:通过allocate()方法分配的缓冲区,建立在JVM内存中
- 直接缓冲区:通过allocateDirect()方法分配的直接缓冲区,建立在物理内存,这样可以提高效率
通过查看allocate()方法源码,我们可以看到new HeapByteBuffer(capacity, capacity),因此印证了allocate是在JVM堆内存中开辟一块空间。
/**
* Allocates a new byte buffer.
*
* <p> The new buffer's position will be zero, its limit will be its
* capacity, its mark will be undefined, and each of its elements will be
* initialized to zero. It will have a {@link #array backing array},
* and its {@link #arrayOffset array offset} will be zero.
*
* @param capacity
* The new buffer's capacity, in bytes
*
* @return The new byte buffer
*
* @throws IllegalArgumentException
* If the <tt>capacity</tt> is a negative integer
*/
public static ByteBuffer allocate(int capacity) {
if (capacity < 0)
throw new IllegalArgumentException();
return new HeapByteBuffer(capacity, capacity);
}
通过查看allocateDirect()方法源码,我们可以看到VM.isDirectMemoryPageAligned()
public static ByteBuffer allocateDirect(int capacity) {
return new DirectByteBuffer(capacity);
}
DirectByteBuffer(int cap) { // package-private
super(-1, 0, cap, cap);
boolean pa = VM.isDirectMemoryPageAligned();
int ps = Bits.pageSize();
long size = Math.max(1L, (long)cap + (pa ? ps : 0));
Bits.reserveMemory(size, cap);
long base = 0;
try {
base = unsafe.allocateMemory(size);
} catch (OutOfMemoryError x) {
Bits.unreserveMemory(size, cap);
throw x;
}
unsafe.setMemory(base, size, (byte) 0);
if (pa && (base % ps != 0)) {
// Round up to page boundary
address = base + ps - (base & (ps - 1));
} else {
address = base;
}
cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
att = null;
}
三、通道
3.1 概念
通道(Channel):通道就是IO源和目标打开的连接,通道本身不存储数据,需要配合缓冲区使用。
3.2 非直接缓冲区 & 直接缓冲区 性能对比
在本地磁盘有一个600M文件,我们用非直接缓冲区 和 直接缓冲区两种方式对其拷贝,看那种方式执行效率更高。
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
/**
* 通道:本身不存储数据,需要配合缓冲区使用
*
* 通道的主要实现类:
* java.nio.channels.Channer接口
* /- FileChannel (本地)
* /- SocketChannel (网络TCP)
* /- ServerSocketChannel (网络TCP)
* /- DatagramChannel (网络UDP)
*
* 如何获取一个通道
* 本地IO:
* FileInputStream/FileOutputStream
* RandemAccessFile
* 网络IO:
* Socket
* ServerSocket
* DatagramSocket
*
*/
public class ChannelTest {
public static void main(String[] args) throws Exception {
testAllocate();
testDirect();
}
/**
* 通道+非直接缓冲区 实现文件的复制
* @throws Exception
*/
public static void testAllocate() throws Exception{
long start = System.currentTimeMillis();
FileInputStream fileInputStream = new FileInputStream("D:/devsoft/test/ideaIU.exe");
FileOutputStream fileOutputStream = new FileOutputStream("D:/devsoft/test/ideaIU_01.exe");
//获取通道
FileChannel fileInputStreamChannel = fileInputStream.getChannel();
FileChannel fileOutputStreamChannel = fileOutputStream.getChannel();
//分配缓冲区大小(非直接缓冲区)
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 将通道中的数据存入到缓冲区
while(fileInputStreamChannel.read(byteBuffer)!=-1){
// 切换到读取模式
byteBuffer.flip();
fileOutputStreamChannel.write(byteBuffer);
byteBuffer.clear();
}
// 关闭资源操作
fileInputStreamChannel.close();
fileOutputStreamChannel.close();
fileInputStream.close();
fileOutputStream.close();
long end = System.currentTimeMillis();
System.out.println("通道+非直接缓冲区,用时:"+(start - end)+"ms");
}
/**
* 通道+直接缓冲区 实现文件的复制
* @throws Exception
*/
public static void testDirect() throws Exception{
long start = System.currentTimeMillis();
//1、打开通道
FileChannel readFileChannel = FileChannel.open(Paths.get("D:/devsoft/test/ideaIU.exe"), StandardOpenOption.READ);
FileChannel writeFileChannel = FileChannel.open(Paths.get("D:/devsoft/test/ideaIU_02.exe"), StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE_NEW);
// 2、获取内存映射文件(直接缓冲区)
MappedByteBuffer readMapperBuffer = readFileChannel.map(FileChannel.MapMode.READ_ONLY, 0, readFileChannel.size());
MappedByteBuffer writeMapperBuffer = writeFileChannel.map(FileChannel.MapMode.READ_WRITE, 0, readFileChannel.size());
// 3、直接对缓冲区进行数据的读写操作
byte[] des = new byte[readMapperBuffer.limit()];
readMapperBuffer.get(des);
writeMapperBuffer.put(des);
// 4、关闭资源操作
readFileChannel.close();
writeFileChannel.close();
long end = System.currentTimeMillis();
System.out.println("通道+直接缓冲区,用时:"+(start - end)+"ms");
}
}
控制台打印结果
通道+非直接缓冲区,用时:-6197ms
通道+直接缓冲区,用时:-1761ms
从执行结果来看,通道+非直接缓冲区,耗时6.1秒;通道+直接缓冲区,耗时1.7秒,很明显,直接缓冲区要比非直接缓冲区读取文件性能更高。
最后给大家送波福利

网友评论