最近在做一个日志组件,考虑到性能和日志的完整性,使用了MMAP,即内存映射的方式写日志,顺便总结一下Java里面的IO操作.
1.IO
IO是阻塞性的,效率不高,但是很容易理解
可以想像成是一个水管,水管里面有空位才能往里写,否则就要等待, 水管里面有水才能读,否则也只能等待.不管IO怎么包装,其操作都是针对字节的.
private static void testIO()throws Exception{
File file = new File("test.txt");
//写
FileOutputStream fileOutputStream = new FileOutputStream(file);
byte[] bytes = "hello world".getBytes();
fileOutputStream.write(bytes);
fileOutputStream.close();
//读
FileInputStream fileInputStream = new FileInputStream(file);
byte[] buffer = new byte[1024];
fileInputStream.read(buffer);
fileInputStream.close();
}
2.NIO
NIO的操作是针对buffer的,且是非阻塞的,因此效率要比IO高.
其中的核心是selector , 一个selector可以监听多个channel , 当每个channel当中有事件产生时,
便会出发selector的事件
//服务端
private static void testNIO_Server() throws Exception{
Selector selector = Selector.open();
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress("127.0.0.1",8000));
serverSocketChannel.configureBlocking(false).register(selector, SelectionKey.OP_ACCEPT);
ByteBuffer readBuff = ByteBuffer.allocate(1024);
ByteBuffer writeBuff = ByteBuffer.allocate(1024);
writeBuff.put("recived it !".getBytes());
writeBuff.flip();
while(true){
int nReady = selector.select();
if(nReady <= 0){
continue;
}
Set<SelectionKey> keys = selector.selectedKeys();
Iterator iterator = keys.iterator();
while(iterator.hasNext()){
SelectionKey selectionKey = (SelectionKey) iterator.next();
iterator.remove();
if(selectionKey.isAcceptable()){
//创建新的连接,并监听read和write事件
SocketChannel socketChannel= serverSocketChannel.accept();
socketChannel.configureBlocking(false).register(selector,SelectionKey.OP_READ);
}else if(selectionKey.isReadable()){
//read事件
readBuff.clear();
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
socketChannel.read(readBuff);
readBuff.flip();
System.out.println("服务端收到的:"+new String(readBuff.array()));
selectionKey.interestOps(SelectionKey.OP_WRITE);
}else if(selectionKey.isWritable()){
//write事件
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
socketChannel.write(writeBuff);
selectionKey.interestOps(SelectionKey.OP_READ);
}
}
}
}
//客户端
private static void testNIO_Client()throws Exception{
ByteBuffer writeBuffer = ByteBuffer.allocate(128);
writeBuffer.put("hello".getBytes());
writeBuffer.flip();
SocketChannel ssc = SocketChannel.open();
boolean connectResult = ssc.connect(new InetSocketAddress("127.0.0.1",8000));
ssc.write(writeBuffer);
}
3.MMAP
MMAP是内存映射,将磁盘上的空间映射到内存当中,性能接近于直接写内存,而且由系统保证能将内存当中的内容写入到磁盘.
MMAP需要事先指定大小,然后往里面进行读写操作.
public static void testMMAP() throws Exception{
File mmapFile = new File("test.txt");
MappedByteBuffer mappedByteBuffer = new RandomAccessFile(mmapFile.getAbsolutePath(), "rw").getChannel().map(FileChannel.MapMode.READ_WRITE, 0, mmapFile.length());
byte[] bytes = "hello world".getBytes();
mappedByteBuffer.put(bytes);
}