一各种IO
BIO(阻塞):资源不可用时,IO请求一直阻塞,直到有结果为止
NIO(非阻塞) :资源不可用时,IO请求离开返回,返回资源不可用
AIO(异步):应用阻塞在发送或者接受数据的状态,直到数据传输成功或者失败
SIO(同步):应用发送成功失败立马返回,实际执行是异步的
阻塞和非阻塞是获取资源的方式,同步和异步是程序设计的方式
我们首先看下 BIO
public class IOServer {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(8000);
// (1) 接收新连接线程
new Thread(() -> {
while (true) {
try {
阻塞方法获取新的连接
Socket socket = serverSocket.accept();
每一个新的连接都创建一个线程,负责读取数据
new Thread(() -> {
try {
byte[] data = new byte[1024];
InputStream inputStream = socket.getInputStream();
while (true) {
int len;
while ((len = inputStream.read(data)) != -1) {
System.out.println(new String(data, 0, len));
}
}
} catch (IOException e) {
}
}).start();
} catch (IOException e) {
}
}
}).start();
}
}
serverSocket.accept()是一个阻塞的方法 没有请求会一直阻塞在那里
public class IOClient {
public static void main(String[] args) {
new Thread(() -> {
try {
Socket socket = new Socket("127.0.0.1", 8000);
while (true) {
try {
socket.getOutputStream().write((new Date() + ": hello world").getBytes());
socket.getOutputStream().flush();
Thread.sleep(2000);
} catch (Exception e) {
}
}
} catch (IOException e) {
}
}).start();
}
}
这样会发生一个问题
同一时刻有大量的线程处于阻塞状态是非常严重的资源浪费
所以java1.4开始提出NIO的思路
NIO三宝也就是三个核心组件是
buffer 缓冲区
channel 管道
selector选择器
接下来我们一个个看
buffer 缓冲区
他本质上是一个可以写入数据的内存块,然后可以再次读取数据
使用buffer写入与读取的四个步骤
1、将数据写入缓冲区
2、调用flip()方法转成读取模式
3、缓冲区读取数据
4、清理缓冲区
Buffer的三个属性
capacity:buffer的容量
position:写入数据的时候是写入的位置,读取时读数据的位置
limit:buffer的容量读取模式下是写入的数据量也就是能读多少数据
看一个例子
public class BufferDemo {
public static void main(String[] args) {
// 构建一个byte字节缓冲区,容量是4
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(4);
// 默认写入模式,查看三个重要的指标
System.out.println(String.format("初始化:capacity容量:%s, position位置:%s, limit限制:%s", byteBuffer.capacity(),
byteBuffer.position(), byteBuffer.limit()));
// 写入2字节的数据
byteBuffer.put((byte) 1);
byteBuffer.put((byte) 2);
byteBuffer.put((byte) 3);
// 再看数据
System.out.println(String.format("写入3字节后,capacity容量:%s, position位置:%s, limit限制:%s", byteBuffer.capacity(),
byteBuffer.position(), byteBuffer.limit()));
// 转换为读取模式(不调用flip方法,也是可以读取数据的,但是position记录读取的位置不对)
System.out.println("#######开始读取");
byteBuffer.flip();
byte a = byteBuffer.get();
System.out.println(a);
byte b = byteBuffer.get();
System.out.println(b);
System.out.println(String.format("读取2字节数据后,capacity容量:%s, position位置:%s, limit限制:%s", byteBuffer.capacity(),
byteBuffer.position(), byteBuffer.limit()));
// 继续写入3字节,此时读模式下,limit=3,position=2.继续写入只能覆盖写入一条数据
// clear()方法清除整个缓冲区。compact()方法仅清除已阅读的数据。转为写入模式
byteBuffer.compact(); // buffer : 1 , 3
byteBuffer.put((byte) 3);
byteBuffer.put((byte) 4);
byteBuffer.put((byte) 5);
System.out.println(String.format("最终的情况,capacity容量:%s, position位置:%s, limit限制:%s", byteBuffer.capacity(),
byteBuffer.position(), byteBuffer.limit()));
// rewind() 重置position为0
// mark() 标记position的位置
// reset() 重置position为上次mark()标记的位置
}
}
Channel通道
Channel包含了TCP/UDP和文件的操作
FileChannel
DatagramChannel
socketChannel
serversocketChannel
和bio区别
channel可以在一个通道进行非阻塞读取和写入
channel替代了IO+NET
selector选择器
selector是java nio的一个组件可以检查一个或者多个nio通道并确定那些通道已经准备好读取或者写入。实现单个线程管理多个通道。从而管理多个连接。
四个事件对于selecttionKey的四个常量
connect(selecttionKey.OP_CONNECT)
accept(selecttionKey.OP_ACCEPT)
read(selecttionKey.OP_READ)
write(selecttionKey.OP_WRITE)
NIO VS BIO