NIO是双向的,非阻塞,NIO面向缓冲区,效率高 ,有通道
IO | NIO |
面向流 | 面向缓冲区 |
阻塞IO | 非阻塞IO |
无 | 选择器(路由) |
目录
Buffer
/**
* 缓冲区 是NIO 提高给传输文件和通道一起使用,存储数据
* Buffer
* ByteBuffer(用的最多)
* LongBuffer
* FloatBuffer
* DoubboBuffer
*/
import java.nio.ByteBuffer;
public class BuffTest {
/** Buffer核心参数
* position :缓冲区正在操作的位置 默认从0开始
* limit;界面(缓冲区可用大小)
* capacity; 暖冲区最大容量,声明后不能改变
* 核心方法:
* put():存放数据
* get()获取数据
*/
@Test
public void test001(){
//初始化ByteBuffer大小
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
System.out.println(byteBuffer.position());//0
System.out.println(byteBuffer.limit());//1024
System.out.println(byteBuffer.capacity());//1024
System.out.println("---------byteBuffer存入数据-------------");
System.out.println();
byteBuffer.put("abc1e".getBytes());
System.out.println(byteBuffer.position());//5
System.out.println(byteBuffer.limit());//1024
System.out.println(byteBuffer.capacity());//1024
System.out.println("--------byteBuffer读取值--------------");
System.out.println();
//开启读取模式
byteBuffer.flip();
System.out.println("position:::"+byteBuffer.position());//0
System.out.println(byteBuffer.limit());//5
System.out.println(byteBuffer.capacity());//1024
byte[] bytes = new byte[byteBuffer.limit()];
byteBuffer.get(bytes);
System.out.println(new String(bytes,0,bytes.length));
System.out.println("---------byteBuffer重复读取-------------");
byteBuffer.rewind();//重复读取上一次下标位置
System.out.println("position:::"+byteBuffer.position());//0
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());//0
System.out.println(byteBuffer.limit());//1024
System.out.println(byteBuffer.capacity());//1024
System.out.println((char) byteBuffer.get());//a
}
}
make(标记)和rest(重置)用法
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
String str="abcd";
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));//ab
System.out.println(byteBuffer.position());//2
System.out.println("------------------");
System.out.println(byteBuffer.position());//2
byteBuffer.get(bytes,2,2);//取两个
System.out.println(new String(bytes,2,2));//cd
byteBuffer.reset();//还原到mark位置
System.out.println("---重置还原标记---");
System.out.println(byteBuffer.position());//2
直接缓冲区(最多用)与非直接缓冲
直接缓冲区和非直接缓冲区(慢,安全)
非直接缓冲区:通过 allocate() 方法分配缓冲区,将缓冲区建立在 JVM 的内存中
直接缓冲区:通过 allocateDirect() 方法分配直接缓冲区,将缓冲区建立在物理内存中。可以提高效率
直接字节缓冲区还可以通过 FileChannel 的 map() 方法 将文件区域直接映射到内存中来创建。该方法返回MappedByteBuffer
每次调用基础操作系统的一个本机 I/O 操作之前(或之后),虚拟机都会尽量避免将缓冲区的内容复制到中间缓冲区中(或从中间缓冲区中复制内容)。
通道
java.nio.channels.Channel 接口:
|--FileChannel
|--SocketChannel
|--ServerSocketChannel
|--DatagramChannel
获取通道
1. Java 针对支持通道的类提供了 getChannel() 方法
本地 IO:
FileInputStream/FileOutputStream
RandomAccessFile
网络IO:
Socket
ServerSocket
DatagramSocket
2. 在 JDK 1.7 中的 NIO.2 针对各个通道提供了静态方法 open()
3. 在 JDK 1.7 中的 NIO.2 的 Files 工具类的 newByteChannel()
//非阻塞NIO
public class Test003 {
//直接缓冲区(用的最多 下面为jdk1.7)
@Test
public void test002() throws IOException {
long starttimeMillis = System.currentTimeMillis();
//创建管道
FileChannel inChannel = FileChannel.open(Paths.get("src/main/resources/1.png"), StandardOpenOption.READ);
FileChannel outChanel = FileChannel.open(Paths.get("src/main/resources/2.png"),StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
//定义映射文件
MappedByteBuffer inMappedByte = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
MappedByteBuffer outMappedByte = outChanel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());
//直接对缓冲区操作
byte[] dsf = new byte[inMappedByte.limit()];
inMappedByte.get(dsf);
outMappedByte.put(dsf);
inChannel.close();
outChanel.close();
System.out.println("操作直接缓冲区完毕");
long endtimeMillis = System.currentTimeMillis();
System.out.println("-------直接缓冲区操作时间------");
System.out.println(endtimeMillis-starttimeMillis);
}
//非直接缓冲区 读写操作
@Test
public void test001() throws IOException {
System.out.println(new File(".").getAbsolutePath());
long starttimeMillis = System.currentTimeMillis();
//读入流
FileInputStream fis = new FileInputStream("src/main/resources/1.png");
//写入流
FileOutputStream fos = new FileOutputStream("src/main/resources/2.png");
//创建通道
FileChannel inchannel = fis.getChannel();
FileChannel outchannel = fos.getChannel();
//分配指定大小缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024);
while (inchannel.read(buf)!=-1){
//开始读取模式
buf.flip();
//将数据写入到通道
outchannel.write(buf);
buf.clear();
}
//关闭通道
inchannel.close();
outchannel.close();
fos.close();
fis.close();
long endtimeMillis = System.currentTimeMillis();
System.out.println("-------非直接缓冲区操作时间------");
System.out.println(endtimeMillis-starttimeMillis);
}
}
分散读取和聚集写入
分散读取(scattering Reads):将通道中的数据分散到多个缓冲区中
聚集写入(gathering Writes):将多个缓冲区的数据聚集到通道中
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class Test004 {
public static void main(String[] args) throws IOException {
//随机访问
RandomAccessFile raf = new RandomAccessFile("F:/mywork/Json_demo/src/main/resources/test.txt","rw");
//获取通道
FileChannel channel = raf.getChannel();//jdk 1.4
//分配指定大小缓冲区
ByteBuffer buf1 = ByteBuffer.allocate(100);
ByteBuffer buf2 = ByteBuffer.allocate(1024);
//分散读取
ByteBuffer[] bufs= {buf1,buf2};
channel.read(bufs);
for (ByteBuffer byteBuffer:bufs) {
//切换成读模式
byteBuffer.flip();
}
System.out.println(new String(bufs[0].array(),0,bufs[0].limit()));
System.out.println("*********************************************");
System.out.println(new String(bufs[1].array(),1,bufs[1].limit()));
System.out.println("---------聚集读取 --------");
// 聚集写入
RandomAccessFile raf2 = new RandomAccessFile("F:/mywork/Json_demo/src/main/resources/test2.txt","rw");
//获取通道
FileChannel channel2 = raf2.getChannel();//jdk 1.4
channel2.write(bufs);
raf2.close();
raf.close();
}
}
字符集 Charset
编码:字符串->字节数组
解码:字节数组 -> 字符串
public class Test005 {
public static void main(String[] args) throws CharacterCodingException {
//获取编码器
Charset charset= Charset.forName("GBK");
//获取加密器
CharsetEncoder ce = charset.newEncoder();
//获取解码器
CharsetDecoder cd = charset.newDecoder();
CharBuffer charBuffer = CharBuffer.allocate(1024);
charBuffer.put("编码棒棒棒");
charBuffer.flip();
//编码加密
ByteBuffer buffer = ce.encode(charBuffer);
for (int i = 0; i < 20; i++) {
System.out.println(buffer.get());
}
//开启读取模式
buffer.flip();
//编码解密
CharBuffer decode = cd.decode(buffer);
System.out.println(decode.toString());
}
}
NIO同步阻塞和非同步阻塞
非阻塞IO:NIO(同步,jdk1.7之前);AIO(异步),只存在网络上
阻塞IO:IO(BIO)同步
伪异步=多线程
伪异步不靠谱:
IO模型关系
NIO客户端和服务端(复杂不灵活)--Netty封装
选择KEY
1、SelectionKey.OP_CONNECT 可连接
2、SelectionKey.OP_ACCEPT 可接受连接
3、SelectionKey.OP_READ 可读
4、SelectionKey.OP_WRITE 可写
如果你对不止一种事件感兴趣,那么可以用“位或”操作符将常量连接起来,如下:
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
在SelectionKey类的源码中我们可以看到如下的4中属性,四个变量用来表示四种不同类型的事件:可读、可写、可连接、可接受连接
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Date;
import java.util.Iterator;
//NIO客户端
class NioClient{
public static void main(String[] args) throws IOException {
System.out.println("-----客户端已启动------");
//1创建socket通道
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",8080));
//2切换异步非阻塞
socketChannel.configureBlocking(false);//jdk1.7以上
//3指定缓冲区大小
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
byteBuffer.put(new Date().toString().getBytes());
//4切换到读取模式
byteBuffer.flip();
socketChannel.write(byteBuffer);
byteBuffer.clear();
//5关闭通道
socketChannel.close();
}
}
//NIO服务器端
class NIOService{
public static void main(String[] args) throws IOException {
System.out.println("服务器端启动-----");
//1创建通道
ServerSocketChannel sChannel = ServerSocketChannel.open();
//2切换异步非阻塞
sChannel.configureBlocking(false);//jdk 1.7以上
//3绑定连接
sChannel.bind(new InetSocketAddress(8080));
//4获取选择器
Selector selector = Selector.open();
//5将通道注册到选择器中 并且监听已经接受的事件
sChannel.register(selector, SelectionKey.OP_ACCEPT);
//6轮询获取”已经准备就绪的“事件
while (selector.select()>0){
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()){
//8获取准备就绪事件
SelectionKey sk = it.next();
//9判断事件准备事件
if (sk.isAcceptable()){
//10若接受就绪,获取客户端连接
SocketChannel socketChannel = sChannel.accept();
//11切换异步非阻塞
socketChannel.configureBlocking(false);//jdk 1.7以上
//12将通道注册到服务器上
socketChannel.register(selector,SelectionKey.OP_READ);
}else if (sk.isReadable()){
//13获取当前选择”就绪状态的通道“
SocketChannel socketChannel =(SocketChannel) sk.channel();
//14读取
int len=0;
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
while ((len=socketChannel.read(byteBuffer))>0){
byteBuffer.flip();
System.out.println("写入时数据:::"+new String(byteBuffer.array(),0,len));
byteBuffer.clear();
}
}
it.remove();
}
}
}
}
public class Client {
}