什么是NIO?
Java NIO(New IO)是一个可以替代标准Java IO API的IO API(从Java 1.4开始),Java NIO提供了与标准IO不同的IO工作方式。
Java NIO: Channels and Buffers(通道和缓冲区)
标准的IO基于字节流和字符流进行操作的,而NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。
Java NIO: Non-blocking IO(非阻塞IO)
Java NIO可以让你非阻塞的使用IO,例如:当线程从通道读取数据到缓冲区时,线程还是可以进行其他事情。当数据被写入到缓冲区时,线程可以继续处理它。从缓冲区写入通道也类似。
Java NIO: Selectors(选择器)
Java NIO引入了选择器的概念,选择器用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个的线程可以监听多个数据通道。
注意:传统IT是单向。 NIO类似
IO和NIO的区别
原有的 IO 是面向流的、阻塞的,NIO 则是面向块的、非阻塞的。
怎么理解IO是面向流的、阻塞的
java1.4以前的io模型,一连接对一个线程。
原始的IO是面向流的,不存在缓存的概念。Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区
Java IO的各种流是阻塞的,这意味着当一个线程调用read或 write方法时,该线程被阻塞,直到有一些数据被读取,或数据完全写入,该线程在此期间不能再干任何事情了。
可以从通道读取数据到缓冲区,也可以把缓冲区的数据写到通道中
buffer中三个关键属性capacity,position以及limit在读写模式中的说明
多个Channel以事件的方式可以注册到同一个Selector,从而达到用一个线程处理多个请求成为可能。
当你调用Selector的select()或者 selectNow() 方法它只会返回有数据读取的SelectableChannel的实例.
区别
IO NIO
面向流 面向缓冲区
阻塞IO 非阻塞IO
无 选择器**
Buffer的概述
1)容量(capacity):表示Buffer最大数据容量,缓冲区容量不能为负,并且建立后不能修改。
2)限制(limit):第一个不应该读取或者写入的数据的索引,即位于limit后的数据不可以读写。缓冲区的限制不能为负,并且不能大于其容量(capacity)。
3)位置(position):下一个要读取或写入的数据的索引。缓冲区的位置不能为负,并且不能大于其限制(limit)。
实例
public static void main(String[] args) {
ByteBuffer byteBuffer= ByteBuffer.allocate(1024); try {
System.out.println(byteBuffer.position());
System.out.println(byteBuffer.limit());
System.out.println(byteBuffer.capacity());
System.out.println("往bytebuffer存放数据:"); byteBuffer.put("你好".getBytes());
System.out.println(byteBuffer.position());
System.out.println(byteBuffer.limit());
System.out.println(byteBuffer.capacity());
System.out.println("-----------------------"); System.out.println("读取值");
byteBuffer.flip();//开启读取模式 System.out.println(byteBuffer.position());
System.out.println(byteBuffer.limit());
System.out.println(byteBuffer.capacity()); byte[] bytes=new
byte[byteBuffer.limit()]; byteBuffer.get(bytes); System.out.println(new
String(bytes,0,bytes.length));
System.out.println("重复读取。。。。。。"); byteBuffer.rewind();//重复读取
System.out.println("position---"+byteBuffer.position());
System.out.println(byteBuffer.limit());
System.out.println(byteBuffer.capacity()); byte[] bytes2=new
byte[byteBuffer.limit()]; byteBuffer.get(bytes2); System.out.println(new
String(bytes2,0,bytes2.length));
System.out.println("清空缓存区。。。。。。"); byteBuffer.clear();
System.out.println("position----"+byteBuffer.position());
System.out.println(byteBuffer.limit());
System.out.println(byteBuffer.capacity()); }catch(Exception e) {
e.printStackTrace(); }
}
4)标记(mark)与重置(reset):标记是一个索引,通过Buffer中的mark()方法指定Buffer中一个特定的position,之后可以通过调用reset()方法恢复到这个position。
实例
@Test
public void testMake() {
ByteBuffer byteBuffer=ByteBuffer.allocate(14);
String str="aksjdlfkasjlkf";
byteBuffer.put(str.getBytes());
//开启读的模式
byteBuffer.flip();
byte[] bytes=new byte[byteBuffer.limit()];
byteBuffer.get(bytes,0,2);
byteBuffer.mark();
System.out.println(new String(bytes,0,2));
System.out.println(byteBuffer.position());
System.out.println("-----------------------------------");
byteBuffer.get(bytes,2,2);
System.out.println(new String(bytes,2,2));
byteBuffer.reset();
System.out.println("重置还原到nake标记");
System.out.println(byteBuffer.position());
}
(缓冲区)buffer 用于NIO存储数据 支持多种不同的数据类型
- 1.byteBuffer
- 2.charBuffer
- 3.shortBuffer
- 4.IntBuffer
- 5.LongBuffer
- 6.FloatBuffer
- 7.DubooBuffer
- 上述缓冲区管理的方式 几乎
- 通过allocate() 获取缓冲区
- 二、缓冲区核心的方法 put 存入数据到缓冲区 get
获取缓冲区数据 flip 开启读模式 - 三、缓冲区四个核心属性
- capacity:缓冲区最大容量,一旦声明不能改变。 limit:界面(缓冲区可以操作的数据大小) limit后面的数据不能读写。
- position:缓冲区正在操作的位置
实例
1、使用nio缓存读取
@Test
public void testFileRead() throws IOException {
long start = System.currentTimeMillis();
//创建管道
FileChannel inChannel = FileChannel.open(Paths.get(“E:\小视频\叶炫清-从前慢.mp3”),StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get(“E:\小视频\叶炫清-从前慢2.mp3”),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 [] bytes=new byte[inMappedByte.limit()];
inMappedByte.get(bytes);
outMappedByte.put(bytes);
inChannel.close();
outChannel.close();
long end = System.currentTimeMillis();
System.out.println(“读取时间”+(start-end));
}
1、使用普通非缓存的
@Test
public void testFileRead2() throws IOException {
long start = System.currentTimeMillis();
//读取文件
FileInputStream fileInputStream = new FileInputStream(“E:\小视频\\叶炫清-从前慢.mp3”);
FileOutputStream fileOutputStream = new FileOutputStream(“E:\小视频\\叶炫清-从前慢2.mp3”);
//创建通道
FileChannel channel = fileInputStream.getChannel();
FileChannel channel2 = fileOutputStream.getChannel();
ByteBuffer allocate = ByteBuffer.allocate(1024);
while(channel.read(allocate)!=-1) {
channel2.write(allocate);
allocate.clear();
}
channel.close();
channel2.close();
fileOutputStream.close();
fileInputStream.close();
long end = System.currentTimeMillis();
System.out.println(“读取时间”+(start-end));
}
通道(Channel)的原理获取
通道表示打开到 IO 设备(例如:文件、套接字)的连接。若需要使用 NIO 系统,需要获取用于连接 IO 设备的通道以及用于容纳数据的缓冲区。然后操作缓冲区,对数据进行处理。Channel 负责传输, Buffer 负责存储。通道是由 java.nio.channels 包定义的。 Channel 表示 IO 源与目标打开的连接。Channel 类似于传统的“流”。只不过 Channel本身不能直接访问数据, Channel 只能与Buffer 进行交互。
分散读取与聚集写入
分散读取(scattering Reads):将通道中的数据分散到多个缓冲区中
实例
@Test
public void testFileRead3() throws IOException {
long start = System.currentTimeMillis();
//读取文件
RandomAccessFile randomAccessFile = new RandomAccessFile("test.txt","rw");
//创建通道--分散读取文件内容
FileChannel channel = randomAccessFile.getChannel();
ByteBuffer allocate = ByteBuffer.allocate(1024);
ByteBuffer allocate2 = ByteBuffer.allocate(1024);
ByteBuffer [] buf2= {allocate,allocate2};
channel.read(buf2);
for (ByteBuffer byteBuffer : buf2) {
//读取模式
byteBuffer.flip();
}
System.out.println(new String(buf2[0].array(),0,buf2[0].limit()));
System.out.println("000000000000000000000000000000000000000000000");
System.out.println(new String(buf2[0].array(),0,buf2[0].limit()));
System.out.println("聚集读取:");
//读取文件
RandomAccessFile randomAccessFile2=new RandomAccessFile("test2.txt", "rw");
//创建通道---聚集写入文件
FileChannel channe2 = randomAccessFile.getChannel();
channe2.write(buf2);
randomAccessFile.close();
randomAccessFile2.close();
long end = System.currentTimeMillis();
System.out.println("读取时间"+(start-end));
}
字符集 Charset
编码:字符串->字节数组
解码:字节数组 -> 字符串
实例
// 获取编码器
Charset cs1 = Charset.forName("GBK");
// 获取编码器
CharsetEncoder ce = cs1.newEncoder();
// 获取解码器
CharsetDecoder cd = cs1.newDecoder();
CharBuffer cBuf = CharBuffer.allocate(1024);
cBuf.put("蚂蚁课堂牛逼!");
cBuf.flip();
// 编码
ByteBuffer bBuf = ce.encode(cBuf);
for (int i = 0; i < 12; i++) {
System.out.println(bBuf.get());
}
// 解码
bBuf.flip();
CharBuffer cBuf2 = cd.decode(bBuf);
System.out.println(cBuf2.toString());
System.out.println("-------------------------------------");
Charset cs2 = Charset.forName("GBK");
bBuf.flip();
CharBuffer cbeef = cs2.decode(bBuf);
System.out.println(cbeef.toString());