BIO:同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
BIO是面向流的
ServerSocket server = new ServerSocket(8888);
//这个方法会阻塞
Socket socket = server.accept();
NIO:同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
NIO是面向缓冲区的
NIO的三个核心:**
1)buffer缓冲区
2)Channel管道
3)Selector选择器
buffer缓冲区和Channel管道
NIO是通过buffer缓冲区和channel管道配合使用来处理数据的
Channel管道比作铁路,buffer缓冲区比作成火车(运载着货物)
而我们的NIO就是通过Channel管道运输着存储数据的Buffer缓冲区的来实现数据的处理
也就是说:Channel不与数据打交道,它只负责运输数据。与数据打交道的是Buffer缓冲区
Channel—>运输
Buffer—>数据
buffer缓冲区的要点
缓冲区就是数组,用于存储不同类型的数据
Buffer是缓冲区的抽象类,根据数据类型的不同,提供了相对应的缓冲区
ByteBuffer、ShortBuffer、IntBuffer、LongBuffer、CharBuffer、FloatBuffer、DoubleBuffer
其中最常用的就是ByteBuffer
缓冲区如何使用:读取缓冲区的数据get(),写数据到缓冲区put()
可以通过flip()方法切换为读模式
几个重要的属性:
1)Capacity容量
缓冲区能够容纳的数据元素的最大数量
容量在缓冲区创建时被设定,并且永远不能被改变(因为底层是数组)
2)Limit界限
写模式情况下,值与capacity一样
读模式下,表示缓冲区里的数据的总数,代表了当前缓冲区中一共有多少数据
3)Position位置
表示将要操作数据的位置
Position会自动由相应的 get( )和 put( )函数更新
4)Mark标记
一个备忘位置,用于记录上一次读写的位置
实例:
public static void main(String[] args) {
// 创建一个缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 看一下初始时4个核心变量的值
System.out.println("初始时-->limit--->"+byteBuffer.limit());
System.out.println("初始时-->position--->"+byteBuffer.position());
System.out.println("初始时-->capacity--->"+byteBuffer.capacity());
System.out.println("初始时-->mark--->" + byteBuffer.mark());
System.out.println("--------------------------------------");
// 添加一些数据到缓冲区中
String s = "testNIO";
byteBuffer.put(s.getBytes());
// 看一下初始时4个核心变量的值
System.out.println("put完之后-->limit--->"+byteBuffer.limit());
System.out.println("put完之后-->position--->"+byteBuffer.position());
System.out.println("put完之后-->capacity--->"+byteBuffer.capacity());
System.out.println("put完之后-->mark--->" + byteBuffer.mark());
}
结果:
通过flip()方法切换为读模式
//切换为读模式
byteBuffer.flip();
// 创建一个limit()大小的字节数组(因为就只有limit这么多个数据可读)
byte[] bytes = new byte[byteBuffer.limit()];
// 将读取的数据装进我们的字节数组中
byteBuffer.get(bytes);
// 输出数据
System.out.println(new String(bytes, 0, bytes.length));
Channel管道要点
channel只负责数据的运输,不直接操作数据
java.nio.channel.Channel接口实现类:
1)本地IO
FileChannel
2)网络IO
SocketChannel、ServerSocketChannel、DategramChannel
获取管道:
通过getChannel()方法
实例:通过FileChannel配合缓冲区实现文件复制
public static void main(String[] args) throws IOException {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("E:/test/a.txt");
fos = new FileOutputStream("E:/test/b.txt");
//获取管道
FileChannel fisChannel = fis.getChannel();
FileChannel fosChannel = fos.getChannel();
//创建缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//将管道中的数据存入缓冲区中
while (fisChannel.read(byteBuffer)!=-1){
//切换为读模式
byteBuffer.flip();
//将缓冲区的数据写入管道
fosChannel.write(byteBuffer);
//清空缓冲区
byteBuffer.clear();
}
//关闭管道
fisChannel.close();
fosChannel.close();
} catch (Exception e) {
e.printStackTrace();
}finally {
if (fis!=null){
fis.close();
}
if (fos!=null){
fos.close();
}
}
}