(补充)关键词
同步是一种可靠的有序运行机制,当我们进行同步操作时,后续的任务是等待当前调用返回,才会进行下一步;
而异步则相反,其他任务不需要等待当前调用返回,通常依靠事件、回调等机制来实现任务间次序关系
在进行阻塞操作时,当前线程会处于阻塞状态,无法从事其他任务,只有当条件就绪才能继续,比如ServerSocket新连接建立完毕,或者数据读取、写入操作完成;
非阻塞则是不管IO操作是否结束,直接返回,相应操作在后台继续处理
package cn.tedu;
import java.nio.ByteBuffer;
public class ByteBufferDemo {
public static void main(String[] args) {
// 创建ByteBuffer
// 创建的时候需要指定容量,实际上就是给底层的数组来指定容量
ByteBuffer buffer = ByteBuffer.allocate(10);
// 添加数据
buffer.put("abc".getBytes());
buffer.put("def".getBytes());
buffer.flip();
byte[] bs = buffer.array();
System.out.println(new String(bs, 0, buffer.limit()));
// 获取数据
// byte b = buffer.get();
// System.out.println(b);
// 如果要遍历这个缓冲区,需要先挪动limit,然后挪动position
// 翻转缓冲区
buffer.flip();
/*
buffer.flip();执行这个方法等同于执行下面的一段代码
先将limit挪到position上
buffer.limit(buffer.position());
将position归零
buffer.position(0);
如果position和limit重合,就表示所有的元素已经取完
while (buffer.position() < buffer.limit())
*/
// while (buffer.position() < buffer.limit()) {
while (buffer.hasRemaining()) {
System.out.println(buffer.get());
}
}
}
BIO - BlockingIO - 同步阻塞式IO
BIO的缺点:
a. 阻塞:相比非阻塞而言,阻塞的效率是相对较低的
b. 一对一的连接:每过来一个客户端,就需要在服务器端去创建一个线程去处理这个请求。这个过程中伴随着大量的 线程的创建和销毁浪费CPU;如果同一时间内,大量客户端产生连接,那么这个时候服务器就得创建大量线程去处理,导 致服务器卡顿甚至崩溃
c. 当客户端连接到服务器之后,即使客户端不发生任何操作,连接依然会保持,这就会导致服务器端的线程会一直被占用
NIO - NewIO - NonBlockingIO - 同步非阻塞式IO
(目前用的比较少) AIO - AsynchronousIO - 异步非阻塞式IO - JDK1.8 - AIO是在NIO的基础上进行了改进,所以AIO又称为NIO.2
BIO |
NIO |
同步阻塞 |
同步非阻塞 |
单向传输数据 |
可以双向传输数据 |
一对一的连接方式 |
一对多的连接方式 |
面向流操作 |
面向缓冲区操作 |
适合于请求少、长连接场景 |
适合于大量请求、短连接的场景 |
Buffer又称之为缓冲区,是用于存储数据的容器
本质上是一个数组,在内存中占用了一块连续的空间
package cn.tedu;
import java.nio.ByteBuffer;
public class ByteBufferDemo {
public static void main(String[] args) {
// 创建ByteBuffer
// 创建的时候需要指定容量,实际上就是给底层的数组来指定容量
ByteBuffer buffer = ByteBuffer.allocate(10);
// 添加数据
buffer.put("abc".getBytes());
buffer.put("def".getBytes());
buffer.flip();
byte[] bs = buffer.array();
System.out.println(new String(bs, 0, buffer.limit()));
// 获取数据
// byte b = buffer.get();
// System.out.println(b);
// 如果要遍历这个缓冲区,需要先挪动limit,然后挪动position
// 翻转缓冲区
buffer.flip();
/*
buffer.flip();执行这个方法等同于执行下面的一段代码
先将limit挪到position上
buffer.limit(buffer.position());
将position归零
buffer.position(0);
如果position和limit重合,就表示所有的元素已经取完
while (buffer.position() < buffer.limit())
*/
// while (buffer.position() < buffer.limit()) {
while (buffer.hasRemaining()) {
System.out.println(buffer.get());
}
}
}
//执行结果
//abcdef
FileChannel:从文件读取数据的
DatagramChannel:读写UDP网络协议数据
SocketChannel:读写TCP网络协议数据
ServerSocketChannel:可以监听TCP连接
package cn.tedu.channel;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
public class SocketServer {
public static void main(String[] args) throws IOException {
// 开启服务器端的通道
ServerSocketChannel ssc = ServerSocketChannel.open();
// 绑定端口
ssc.bind(new InetSocketAddress(8090));
ssc.configureBlocking(false);
// 接受连接
SocketChannel sc = ssc.accept();
while (sc == null)
sc = ssc.accept();
// 读取数据
ByteBuffer buffer = ByteBuffer.allocate(1024);
sc.read(buffer);
System.out.println(new String(buffer.array(), 0, buffer.position()));
sc.write(ByteBuffer.wrap("hello wyf".getBytes()));
// 关流
ssc.close();
}
}
//输出
//hello server
package cn.tedu.channel;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class SocketClient {
public static void main(String[] args) throws IOException, InterruptedException {
// 开启客户端通道
SocketChannel sc = SocketChannel.open();
// 设置为非阻塞
sc.configureBlocking(false);
// 发起连接
sc.connect(new InetSocketAddress("localhost", 8090));
while (!sc.isConnected())
// 试图再次建立连接并且自动计数
// 当达到底层的计数阈值之后,就会认为这个连接无法建立
// 这个时候就会报错
sc.finishConnect();
// 写出数据
sc.write(ByteBuffer.wrap("hello server".getBytes()));
Thread.sleep(7);
ByteBuffer buffer = ByteBuffer.allocate(1024);
sc.read(buffer);
System.out.println(new String(buffer.array(), 0, buffer.position()));
// 关流
sc.close();
}
}
//输出
//hello wyf
package cn.tedu.selector;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class Server {
public static void main(String[] args) throws IOException {
// 开启服务器的通道
ServerSocketChannel ssc = ServerSocketChannel.open();
// 绑定端口
ssc.bind(new InetSocketAddress(8070));
// 设置为非阻塞
ssc.configureBlocking(false);
// 开启选择器
Selector selc = Selector.open();
// 将服务器注册到选择器上
ssc.register(selc, SelectionKey.OP_ACCEPT);
while (true) {
// 进行选择
selc.select();
// 获取选择出来的事件
Set keys = selc.selectedKeys();
// 遍历集合,根据事件类型不同来进行对应的处理
Iterator it = keys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
// 判断事件类型
// 可接受事件
if (key.isAcceptable()) {
// 从这个事件中获取通道
ServerSocketChannel sscx = (ServerSocketChannel) key.channel();
// 接受连接
SocketChannel sc = sscx.accept();
System.out.println("接到一个连接~~~");
// 设置非阻塞
sc.configureBlocking(false);
// 注册读/写事件
// 后一次的注册事件会覆盖前一次的注册事件
// sc.register(selc, SelectionKey.OP_READ -
// SelectionKey.OP_WRITE);
sc.register(selc, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
}
// 可读事件
if (key.isReadable()) {
// 获取到通道
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
sc.read(buffer);
System.out.println(new String(buffer.array(), 0, buffer.position()));
// 移除掉可读事件
// 获取到这个通道身上的所有事件
sc.register(selc, key.interestOps() ^ SelectionKey.OP_READ);
// sc.register(selc, key.interestOps() -
// SelectionKey.OP_READ);
}
// 可写事件
if (key.isWritable()) {
SocketChannel sc = (SocketChannel) key.channel();
sc.write(ByteBuffer.wrap("hello client~~~".getBytes()));
// 移除掉这个可写事件
sc.register(selc, key.interestOps() - SelectionKey.OP_WRITE);
}
it.remove();
}
}
}
}
package cn.tedu.selector;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class Client {
public static void main(String[] args) throws IOException {
SocketChannel sc = SocketChannel.open();
sc.connect(new InetSocketAddress("localhost", 8070));
sc.write(ByteBuffer.wrap("hello server".getBytes()));
ByteBuffer buffer = ByteBuffer.allocate(1024);
sc.read(buffer);
System.out.println(new String(buffer.array(), 0, buffer.position()));
sc.close();
}
}