Java中的NIO和BIO
首先我们先了解一下,阻塞(Block)和非阻塞(Non-Block). 阻塞:往往需要等待数据缓冲区的数据准备好以后才处理其它事情,否则一致等待在哪里。 非阻塞:当进程访问我们的数据缓冲区的时候,如果数据没有准备好就立即返回,不会等待。如果数据以及准备好,也直接返回。
阻塞和非阻塞是进程在访问数据缓冲区的时候,数据是否准备就绪的一种处理方式。
同步: 在同一时间只能做一件事情。 异步: 在处理数据的时候,同一时间点能做多个处理。
NIO:同步非阻塞IO。 BIO:同步阻塞IO。 AIO:异步非阻塞IO。
BIO原理
public class Server {
final static int PROT = 8765;
public static void main(String[] args) {
ServerSocket server = null;
try {
server = new ServerSocket(PROT);
System.out.println(" server start .. ");
Socket socket = server.accept();
new Thread(new ServerHandler(socket)).start();
} catch (Exception e) {
e.printStackTrace();
} finally {
if(server != null){
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
server = null;
}
}
BIO会在代码 Socket socket = server.accept();处进行阻塞,这个时候正在等待连接和接收数据。
NIO原理
public class Server implements Runnable{
private Selector seletor;
private ByteBuffer readBuf = ByteBuffer.allocate(1024);
private ByteBuffer writeBuf = ByteBuffer.allocate(1024);
public Server(int port){
try {
this.seletor = Selector.open();
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.bind(new InetSocketAddress(port));
ssc.register(this.seletor, SelectionKey.OP_ACCEPT);
System.out.println("Server start, port :" + port);
} catch (IOException e) {
e.printStackTrace();
}
}
private void accept(SelectionKey key) {
try {
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
sc.register(this.seletor, SelectionKey.OP_READ);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while(true){
try {
this.seletor.select();
Iterator<SelectionKey> keys = this.seletor.selectedKeys().iterator();
while(keys.hasNext()){
SelectionKey key = keys.next();
keys.remove();
if(key.isValid()){
if(key.isAcceptable()){
this.accept(key);
}
if(key.isReadable()){
this.read(key);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void write(SelectionKey key){
}
private void read(SelectionKey key) {
try {
this.readBuf.clear();
SocketChannel sc = (SocketChannel) key.channel();
int count = sc.read(this.readBuf);
if(count == -1){
key.channel().close();
key.cancel();
return;
}
this.readBuf.flip();
byte[] bytes = new byte[this.readBuf.remaining()];
this.readBuf.get(bytes);
String body = new String(bytes).trim();
System.out.println("Server : " + body);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new Thread(new Server(8765)).start();;
}
}
NIO多了一个ByteBuffer缓冲区,所以实现了非阻塞。 NIO底层利用了select()/epoll()。
select()与epoll()
早期1.6版本,NIO底层实现用的是select()。 jdk1.8以后NIO底层用的是epoll()。
select()与epoll()、poll的区别
select():—>O(n) 它仅仅知道有I/O事件发生了,却不知道具体是哪个流。只能做轮训,找出能够读出数据或写入数据的流。 select()具有O(n)的无差别轮训的复杂度。
poll():—>O(n) poll()本质上和select()没有区别,它将用户传入的数据从用户空间拷贝到内核空间。 相比select()它没有最大连接数限制(底层是由链表实现的)。
epoll():—>时间复杂度O(1) epoll()会把哪个流发生了I/O事件通知我们,所以epoll()是基于事件驱动的。
1)select()、poll()、epoll()都是I/O多路复用机制,可以监视多个文件描述符。一旦描述符就绪,就能通知程序进行相应的读写操作。 2)select()、poll()、epoll() 本质上都是同步I/O,因为他们都需要在读写事件就绪后,自己负责进行读写。这个读写过程是 ‘‘阻塞的’’。非阻塞指的是,数据流在写入、读取ByteBuffer的时候。 3)AIO(异步非阻塞I/O):无需自己负责进行读写,异步I/O会把数据从内核拷贝到用户空间。
select()缺点
1)单个进程能够监听的文件描述符fd有限,默认是1024。 2)每次调用select都会把描述符fd由内核空间---->用户空间的拷贝。 3)使用轮训的方式,需要传进来所有的fd。
epoll()优点
没有监听的Fd描述符,远大于1024.一般1G内存,默认是10万。
|