目录
1、继承关系
2、内存管理
3、MappedByteBuffer效率比较
4、性能分析
5、注意
public static void main(String[] args) throws Exception {
File file = new File("F:\\Download\\ideaIU-2019.1-jbr11.win.zip");
long len = file.length();
try {
MappedByteBuffer mappedByteBuffer = new RandomAccessFile(file, "r")
.getChannel()
.map(FileChannel.MapMode.READ_ONLY, 0, len);
long start = System.currentTimeMillis();
for (int offset = 0; offset < len; offset++) {
mappedByteBuffer.get();
}
System.out.println("MappedByteBuffer : "+(System.currentTimeMillis() - start));
} catch (IOException e) {
}
try {
FileInputStream fis = new FileInputStream(file);
long start = System.currentTimeMillis();
for (int offset = 0; offset < len/1000; offset++) {
fis.read();
}
System.out.println("Stream : "+(System.currentTimeMillis() - start));
} catch (IOException e) {
}
try {
RandomAccessFile aFile = new RandomAccessFile("F:\\Download\\ideaIU-2019.1-jbr11.win.zip","rw");
FileChannel channel = aFile.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1);
long start = System.currentTimeMillis();
for (int offset = 0; offset < len/100; offset++) {
channel.read(buffer);
}
System.out.println("FileChannel : "+(System.currentTimeMillis() - start));
} catch (IOException e) {
}
try {
RandomAccessFile aFile = new RandomAccessFile("F:\\Download\\ideaIU-2019.1-jbr11.win.zip","rw");
long start = System.currentTimeMillis();
for (int offset = 0; offset < len/1000; offset++) {
aFile.read();
}
System.out.println("FileChannel : "+(System.currentTimeMillis() - start));
} catch (IOException e) {
}
}
MappedByteBuffer : 391
Stream : 759
FileChannel : 882
每次都读取1个字节数据,从结果可以看出Stream、FileChannel、MappedByteBuffer的效率,还是差别很大的
从代码层面上看,从硬盘上将文件读入内存,都要经过文件系统进行数据拷贝,并且数据拷贝操作是由文件系统和硬件驱动实现的,理论上来说,拷贝数据的效率是一样的。
但是通过内存映射的方法访问硬盘上的文件,效率要比read和write系统调用高,这是为什么?
所以,采用内存映射的读写效率要比传统的read/write性能高。
1、缓冲区类型:
private int mark = -1;
private int position = 0;
private int limit;
private int capacity;
每个缓冲区都有这4个属性,无论缓冲区是何种类型都有相同的方法来设置这些值
2、ByteBuffer 基本操作
import java.nio.ByteBuffer;
public class MainNioTest {
public static void main(String[] args) {
//初始化
ByteBuffer buffer = ByteBuffer.allocate(10);
//保存
buffer.put("1234567".getBytes());
//反转(读写转换)
buffer.flip();
//读取
while (buffer.hasRemaining()) {
System.out.print((char)buffer.get());
}
}
}
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
每次从写转为读状态时,都需要调用flip() 方法,用来移动 position的位置,到缓存的开始
3、ByteBuffer 继承类
HeapByteBuffer 分配在jvm堆内存
DirectByteBuffer 直接分配在内存
DirectByteBufferR 返回只一个只读对象,无法写操作
MappedByteBuffer 大文件操作
4、Channel通道类型
FileChannel:从文件中读写数据
DatagramChannel:能通过UDP读写网络中的数据。
SocketChannel:能通过TCP读写网络中的数据。
ServerSocketChannel:可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel
5、Buffer和Channel
import java.io.File;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class MainNIoChannel {
public static void main(String[] args) throws Exception {
String path = "D:"+File.separator+"data.txt";
RandomAccessFile aFile = new RandomAccessFile(path,"rw");
FileChannel channel = aFile.getChannel();//得到管道
//借助一个Buffer来与Channel进行交互
ByteBuffer buffer = ByteBuffer.allocate(1025);
int readByteLen;
while((readByteLen = channel.read(buffer)) != -1){
System.out.println("读取到buffer中的数据长度为:"+readByteLen);
System.out.println("内容如下:");
//将Buffer从写模式切换到读模式
buffer.flip();
while(buffer.hasRemaining()){
System.out.print((char)buffer.get());
}
buffer.clear();
System.out.println();//换行
}
aFile.close();
}
}
6、Selector
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class MainSelectorServer {
public static void main(String[] args) throws Exception {
MainSelectorServer server = new MainSelectorServer();
server.init(9999);
server.listen();
}
// 通道选择器
private Selector selector;
public MainSelectorServer(){
try {
selector = Selector.open();
} catch (IOException e) {
e.printStackTrace();
}
}
/*
* 函数功能:服务器端开始监听,看是否有客户端连接进来
*/
private void listen() throws Exception {
System.out.println("server running....");
while (true) {
// 当注册事件到达时,方法返回,否则该方法会一直阻塞
selector.select();
// 获得selector中选中的相的迭代器,选中的相为注册的事件
Set set = selector.selectedKeys();
Iterator ite = set.iterator();
while (ite.hasNext()) {
SelectionKey selectionKey = (SelectionKey) ite.next();
// 删除已选的key 以防重负处理
ite.remove();
if (selectionKey.isAcceptable()) {// 如果有客户端连接进来
// 先拿到这个SelectionKey里面的ServerSocketChannel。
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
SocketChannel socketChannel = serverSocketChannel.accept();
System.out.println("有客户端连接到服务器!!!");
socketChannel.configureBlocking(false);// 将此通道设置为非阻塞
Thread.sleep(2000);
socketChannel.write(ByteBuffer.wrap(new String("hello client!").getBytes()));
socketChannel.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) {// 客户端发送数据过来了
// 先拿到这个SelectionKey里面的SocketChannel。
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
// 接收来自于客户端发送过来的数据
ByteBuffer buf = ByteBuffer.allocate(13);
while (socketChannel.read(buf) != -1) {
byte[] receData = buf.array();
String msg = new String(receData).trim();
Thread.sleep(2000);
System.out.println("接收来自客户端的数据为:" + msg);
buf.clear();
}
}
}
}
}
/*
* 函数功能:初始化serverSocketChannel来监听指定的端口是否有新的TCP连接,
* 并将serverSocketChannel注册到selector中
*/
private void init(int port) {
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// serverSocketChannel监听指定端口
serverSocketChannel.socket().bind(new InetSocketAddress(port));
serverSocketChannel.configureBlocking(false);// 设置为非阻塞模式
// 将serverSocketChannel注册到selector中,并为该通道注册selectionKey.OP_ACCEPT事件
// 注册该事件后,当事件到达的时候,selector.select()会返回, 如果事件没有到达selector.select()会一直阻塞
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class MainSelectorClient {
public static void main(String[] args) throws Exception {
MainSelectorClient client = new MainSelectorClient();
client.init("localhost", 9999);
client.connect();
}
private Selector selector;
public MainSelectorClient() throws IOException {
this.selector = Selector.open();
}
private void init(String address, int port) throws IOException {
// 客户端,首先有一个SocketChannel
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);// 将此通道设置为非阻塞模式
socketChannel.connect(new InetSocketAddress(address, port));
// 将SocketChannel注册到selector中,并为该通道注册SelectionKey.OP_CONNECT
socketChannel.register(selector, SelectionKey.OP_CONNECT);
}
private void connect() throws Exception {
while (true) {
selector.select();
Set set = selector.selectedKeys();
Iterator ite = set.iterator();
while (ite.hasNext()) {
SelectionKey selectionKey = (SelectionKey) ite.next();
ite.remove(); // 删除已选的key,以防重复处理
if (selectionKey.isConnectable()) {// 看是否有连接发生
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
// 如果正在连接,则完成连接
if (socketChannel.isConnectionPending()) {
socketChannel.finishConnect();
}
socketChannel.configureBlocking(false);// 设置为非阻塞模式
// 给服务器端发送数据
System.out.println("客户端连接上了服务器端。。。。");
Thread.sleep(2000);
socketChannel.write(ByteBuffer.wrap(new String("hello server!").getBytes()));
// 为了接收来自服务器端的数据,将此通道注册到选择器中
socketChannel.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) {
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
// 接收来自于服务器端发送过来的数据
ByteBuffer buf = ByteBuffer.allocate(13);
while (socketChannel.read(buf) != -1) {
byte[] receData = buf.array();
String msg = new String(receData).trim();
Thread.sleep(2000);
System.out.println("接收来自服务器端的数据为:" + msg);
buf.clear();
}
}
}
}
}
}