BIO 朝NIO的演变过程
此种情况下,客户端发送一次数据以后,服务器端就会停止
public class OnceSocketTcpServer {
static byte[] buffer = new byte[1024];
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(8080))
Socket socket = serverSocket.accept();
int read = socket.getInputStream().read(buffer);
String result = new String(buffer);
System.out.println(result);
} catch (IOException e) {
e.printStackTrace();
}
}
}
现在可以支持多个请求,但是,由于单线程执行,某个方法执行时间太久会造成程序的阻塞
public class OnceSocketTcpServer {
static byte[] buffer = new byte[1024];
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(8080));
while(true) {
Socket socket = serverSocket.accept();
int read = socket.getInputStream().read(buffer);
String result = new String(buffer);
System.out.println(result);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
在伪异步的情况下,开启多线程的方式来处理。但是,并发的连接数过大,开启的线程过多。cpu资源占用太多
public class OnceSocketTcpServer {
static byte[] buffer = new byte[1024];
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(8080));
while(true) {
final Socket socket = serverSocket.accept();
int read = socket.getInputStream().read(buffer);
new Thread(new Runnable() {
public void run() {
String result = new String(buffer);
System.out.println(result);
}
}).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
即时采用线程池的情况下,频繁的切换线程也会造成cpu资源的消耗和浪费
public class OnceSocketTcpServer {
static byte[] buffer = new byte[1024];
public static void main(String[] args) {
try {
ExecutorService executorService = Executors.newCachedThreadPool();
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(8080));
while(true) {
final Socket socket = serverSocket.accept();
int read = socket.getInputStream().read(buffer);
executorService.execute(new Runnable() {
public void run() {
String result = new String(buffer);
System.out.println(result);
}
});
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
什么是NIO
三个概念:
这里先抛砖引玉,为什么redis是单线程,却能够支持高并发
我们需要提到一个概念,多路IO复用机制,这是什么意思呢?
多路:实际上指的就是多个Tcp连接,即上述的多个管道
IO复用:将多个Tcp连接(管道)统一交给一个Selector选择器进行管理。最后,统一使用buffer将数据写入硬盘。
buffer的作用是什么呢
每次单独写入磁盘,效率会特别底下,所以,将数据汇集的多一些以后一起写入,可以提高效率
Bio和Nio到底有什么区别
Bio
在数据没有内核空间的时候,程序会一直阻塞,这个时候cpu放弃了使用权,不能干其他事情
Nio
不管有没有拿到数据都会立即返回结果,如果返回的结果没有数据,会循环请求数据,如果拿到了数据,程序继续执行。这种情况下程序并不会阻塞
Nio、Selector、Channel、Buffer原理
Nio架构流程图
Nio的实现步骤
此种实现方式还有一些缺陷,如果客户端断开连接,需要将该连接从Selector选择器中移除。
当然,此处实现还是颇有问题,当某些客户端已经连接上,即存在于Selector中,但是,一直没有发送消息,如果这样的连接过多,也会造成大量的资源浪费。
public class OnceSocketTcpServer {
static ByteBuffer byteBuffer = ByteBuffer.allocate(512);
static List<SocketChannel> selectors = new ArrayList<>();
public static void main(String[] args) {
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8080));
// 设置为异步接收
serverSocketChannel.configureBlocking(false);
while(true) {
// 接收管道内的数据
SocketChannel socketChannel = serverSocketChannel.accept();
// 如果数据不为空,则讲管道添加到selector选择器中
if (socketChannel != null) {
socketChannel.configureBlocking(false);
selectors.add(socketChannel);
}
// 遍历选择器中的所有管道,看是否已经发送数据
for (SocketChannel sel : selectors) {
int j = sel.read(byteBuffer);
if (j > 0) {
byteBuffer.flip();
byte[] bytes = Arrays.copyOf(byteBuffer.array(), byteBuffer.limit());
System.out.println("已经获取到了数据" + new String(bytes));
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
IO复用机制、信号驱动IO、异步IO的区别