NIO即New IO,这个库是在JDK1.4中才引入的。NIO和IO有相同的作用和目的,但实现方式不同,NIO主要用到的是块,所以NIO的效率要比IO高很多。
Q: NIO和标准IO有什么区别?
A:
Q: 通道Channel对象能同时做读写操作吗?
还是说需要像标准IO那样,需要同时创建input和output对象才能做读写操作?
A:
通道Channel是双向的, 既可以从channel中读数据,也可以写数据。
可以看到既能调用read也能调用write,且需要依赖缓冲区buffer。
FileChannel fileChannel = FileChannel.open(new File("a.txt").toPath());
ByteBuffer buf = ByteBuffer.allocate(1024);
fileChannel.read(buf);
fileChannel.write(buf);
Q: 通道支持异步读写吗
A: 支持。
Q: 通道的读写是否必须要依赖缓冲区buffer?
A: 一般都是依赖buffer的。 但也支持2个管道之间的传输,即管道之间直接读写。
String[] arr=new String[]{"a.txt","b.txt"};
FileChannel in=new FileInputStream(arr[0]).getChannel();
FileChannel out =new FileOutputStream(arr[1]).getChannel();
// 将a.txt中的数据直接写进b.txt中,相当于文件拷贝
in.transferTo(0, in.size(), out);
常用的几种Channel
Java NIO中的FileChannel是一个连接到文件的通道。可以通过文件通道读写文件。
FileChannel无法设置为非阻塞模式,它总是运行在阻塞模式下
创建方式
RandomAccessFile file = new RandomAccessFile("D:/aa.txt");
FileChannel fileChannel = file.getChannel();
Java NIO中的SocketChannel是一个连接到TCP网络套接字的通道。
支持非阻塞模式socketChannel.configureBlocking(false)。
可以通过以下2种方式创建SocketChannel:
打开一个SocketChannel并连接到互联网上的某台服务器。
一个新连接到达ServerSocketChannel时,会创建一个SocketChannel
创建方式
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("192.168.1.100",80));
Java NIO中的 ServerSocketChannel 是一个可以监听新进来的TCP连接的通道, 就像标准IO中的ServerSocket一样。ServerSocketChannel类在 java.nio.channels包中。
SocketChannel和ServerSocketChannel的区别: 前者用于客户端,后者用于服务端
创建方式:
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket.bind(new InetSocketAddress(80));
serverSocketChannel.configureBlocking(false);
while(true){
SocketChannel socketChannel = serverSocketChannel.accept();
if(socketChannle != null)
doSomething...
}
ByteBuffer buf = ByteBuffer.allocate(1024);
Q: buffer调用flip()方法从写模式切换到读模式时,position会变成多少?
A: 变为0。
ByteBuffer buf = ByteBuffer.allocate(1024);
// 数据读到buf中,并返回数量,每次最多读1024个
int byteRead = fileChannel.read(buf);
// 输出byteRead的数量,最多为1024
System.out.println("position=" + buf.position()+", byteRead=" + byteRead);
buf.flip();
// 切换到读模式了,输出0
System.out.println("position=" + buf.position());
ByteBuffer buf = ByteBuffer.allocate(1024);
// 数据读到buf中,并返回数量,每次最多读1024个
int byteRead = fileChannel.read(buf);
// 输出byteRead的数量,最多为1024
System.out.println("position=" + buf.position()+", byteRead=" + byteRead);
buf.flip();
// 切换到读模式了,输出0
System.out.println("position=" + buf.position());
结果如下
Q: 向buf缓冲区写数据的方式有哪些?
A:
Q: 从buf缓冲区读数据的方式有哪些?
Q: 手动修改当前缓冲区的postion的方法有哪些?
A:
Q:1个channel管道支持多个buffer吗?
A: 支持。 通道的write和read方法都支持传入1个buffer数组,会按照顺序做读写操作。
Buffer的种类:
Buffer的另外3个方法:
selector可用来在线程中关联多个通道,并进行事件监听。
Q: 在NIO中Selector的好处是什么?
A:
Q: Selector支持注册哪种类型的通道?
A:
支持非阻塞的通道。
通道要在注册前调用 channel.configureBlocking(false) 设置为非阻塞。
例如FileChannel就没办法注册,他注定是阻塞的。
而socketChannel就可以支持非阻塞。
Q: Selector注册时,支持监听哪几种事件,对应的常量是什么?(啊最不喜欢记忆这种东西了…)
A:
共有4种可监听事件
int interestingSet = Selectionkey.OP_READ | Selectionkey.OP_WRITE;
Selectionkey key = channel.register(selector,interestingSet)
Q: Selector维护的SelectionKey集合共有哪几种?
A:
共有三种。
1.已注册的所有键的集合(Registered key set)
所有与选择器关联的通道所生成的键的集合称为已经注册的键的集合。并不是所有注册过的键都仍然有效。这个集合通过keys()方法返回,并且可能是空的。这个已注册的键的集合不是可以直接修改的;试图这么做的话将引发java.lang.UnsupportedOperationException。
2.已选择的键的集合(Selected key set)
已注册的键的集合的子集。这个集合的每个成员都是相关的通道被选择器(在前一个选择操作中)判断为已经准备好的,并且包含于键的interest集合中的操作。这个集合通过selectedKeys()方法返回(并有可能是空的)。
不要将已选择的键的集合与ready集合弄混了。这是一个键的集合,每个键都关联一个已经准备好至少一种操作的通道。每个键都有一个内嵌的ready集合,指示了所关联的通道已经准备好的操作。键可以直接从这个集合中移除,但不能添加。试图向已选择的键的集合中添加元素将抛出java.lang.UnsupportedOperationException。
3.已取消的键的集合(Cancelled key set)
已注册的键的集合的子集,这个集合包含了cancel()方法被调用过的键(这个键已经被无效化),但它们还没有被注销。这个集合是选择器对象的私有成员,因而无法直接访问。
注册之后, 如何使用selector对准备就绪的通道做处理:
while(true) {
int readyNum = selector.select();
if (readyNum == 0) {
continue;
}
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> it = selectedKeys.iterator();
while(it.hasNext()) {
SelectionKey key = it.next();
if(key.isAcceptable()) {
// 接受连接
} else if (key.isReadable()) {
// 通道可读
} else if (key.isWritable()) {
// 通道可写
}
it.remove();
}
}
Q:
select()方法其实是阻塞方法,即调用时会进入等待,直到把所有通道都轮询完毕。
如果希望提前结束select(),有哪些方法?
A:
有2个办法:
wakeup(), 调用后,select()方法立刻返回。
close(), 直接关闭selector。
PS: 之前说NIO是非阻塞IO,但为什么上面却说select()方法是阻塞的?
Q: 多线程读写同一文件时,如何加锁保证线程安全?
A:
使用FileChannel的加锁功能。
本文系作者在时代Java发表,未经许可,不得转载。
如有侵权,请联系nowjava@qq.com删除。