NIO与BIO
IO inputStream outStream 文件操作流
网络流
通信:就是将数据写到相应的端口(socket)
等待客户端链接(accept())返回一个socket,
通过socket进行接受数据(getInputStream()) 缓冲流(输入流(getInputStream))
client
应用场景
设备与server间 的通信
心跳的通信
及时状态的上报
服务端与客户端的远程连接的操作。(服务端要反向的向客户端进行数据的返回)
双向的通信
BIO中的阻塞
accept()方法是阻塞的
readLine()第二次阻塞(会将后面的连接堵塞):只要是第一个没有完成,就会一直的等下去
改进:多家几个,处理对象(多线程)
采用线程池
public class Server {
public static void main(String[] args) {
int port = 8080;
// 声明线程池
BlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<>();
Executor executor = new ThreadPoolExecutor(10, 10000, 5,
TimeUnit.SECONDS, blockingQueue);
// 创建一个服务端,这里使用了Java 7的try-with-resource简化语法
try(ServerSocket serverSocket = new ServerSocket(port)) {
System.out.println("server 启动...");
while (true) { // 死循环
// 这里会阻塞,直到有新的连接进来
Socket socket = serverSocket.accept();
// 有新的连接,就创建一个新的Task,丢进线程池。
executor.execute(() -> {
try(BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
// 读取并打印数据
System.out.println("server 收到: " + reader.readLine());
} catch (IOException e) {
e.printStackTrace();
}
});
}
} catch (IOException e) {
e.printStackTrace();
}
}
」}
public class Client {
public static void main(String[] args) {
int port = 8080;
try(
Socket socket = new Socket("127.0.0.1", 8080);
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
) {
writer.println("hi,这是client");
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用Java多线程和BIO的网络通信方式,是Tomcat的基本原理。它的优势是模型简单,很容易就能实现,而且也能很好的工作。不过也有一定的劣势,就是不能承受太大的并发量。
一方面一旦并发量超过阻塞队列的容量,线程池就会执行拒绝策略;另一方面,每个线程在处理IO的时候其实是阻塞的,一旦处理IO的时间过长,就会阻塞很久,导致后面的Task需要等很久。
下面我们将用NIO来实现相同的功能。
读IO流时:
流 的本质是阻塞的!
管家告诉客人哪个是空闲的,能够进行通信,
读流的操作,一定是阻塞的,所以只需将用户的数据通过通道,存到buffer中,之后对buffer进行读取操作。实现,不关心谁的消息,
selector是event
NIO特性new io 1.4可非阻塞IO
java.nio包
管家、通道、缓存
解决BIO阻塞,
NIO工作流程
通过事件判断所有注册在channel上的 小姐姐(各种变化)channel有自己的变化,有相应的变化的事件
具体要去干什么,交给ThreadPool
why:
提高并发、节约成本
互联网的高并发请求:对外的web服务器,内部的为服务。
tomcat 由BIO----> 改成NIO
NIO使用
channel要注册到selector上
通过selector,一个线程能够处理多个channel,可以极大的减少线程的数量。使用CPU核心数量的线程,充分利用cpu资源,减少线程间的切换。
1、创建管家
2、将channel注册到selector上
3、selector.select()
4、获取SelectionKey
5、处理selectorKeys
selectionKey是一个对象。其中存着各种准备就绪的channel
对keys进行迭代,拿到具体的哪个channel
有四个事件,哪个事件做不同的事。针对不同的事件做不同是事。
flip方法 flip方法执行完之后,再去读数据就能够读了