Nio与IO的区别

  原有的 IO 是面向流的、阻塞的,NIO 则是面向块的、非阻塞的。

   1.IO流每次从流中读一个或多个字节,直至读完所有字节,他们没有被缓存在其他地方,并且,IO流不能移动流中的数据,如果需要前后移动从流中读取的教据,需要先将它缓存到一个缓冲区。Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,霱要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数裾。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。

   2.Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。 Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。、

通道Channel

  NIO的通道类似于流,但有些区别如下:

    1. 通道可以同时进行读写,而流只能读或者只能写

    2. 通道可以实现异步读写数据

    3. 通道可以从缓冲读数据,也可以写数据到缓冲:

缓冲区Buffer类

  缓冲区是一个对象,有四个基本属性,Nio的读写都是利用Buffer来实现的,简而言之,读取数据先从缓冲区读,写数据也是先写入缓冲区的。我们最常用的是ByteBuffer这个实现类,对于Java中的基本类型都有一个对应的Buffer实现类与之对应,如CharBuffer,DoubleBuffer等等

  1丶其中的四个属性的含义分别如下:
    容量(Capacity):缓冲区能够容纳的数据元素的最大数量。这一个容量在缓冲区创建时被设定,并且永远不能改变。
    上界(Limit):缓冲区的第一个不能被读或写的元素。或者说,缓冲区中现存元素的计数。
    位置(Position):下一个要被读或写的元素的索引。位置会自动由相应的 get( )和 put( )函数更新。
    标记(Mark):下一个要被读或写的元素的索引。位置会自动由相应的 get( )和 put( )函数更新。
          2丶Buffer的常见方法如下所示:  

    flip(): 写模式转换成读模式
    rewind():将 position 重置为 0 ,一般用于重复读。
    clear() :
    compact(): 将未读取的数据拷贝到 buffer 的头部位。
    mark(): reset():mark 可以标记一个位置, reset 可以重置到该位置

   3丶读取操作

 FileInputStream inputStream = new FileInputStream("E:\\A.txt");
/**
* 拿到通道
*/
FileChannel channel = inputStream.getChannel(); /**
* 创建缓存区
*/
ByteBuffer buffer = ByteBuffer.allocate(1024); /**
* 读取数据到缓冲区
*/
channel.read(buffer); buffer.flip(); while (buffer.remaining() > 0){
byte b = buffer.get();
System.out.println(((char)b));
}
/**
* 关闭流
*/
inputStream.close();

4丶写入操作

 static private final byte message[] = { 83,83,83,83,83,83 };

     static public void main( String args[] ) throws Exception {
FileOutputStream fout = new FileOutputStream( "e:\\A.txt" ); FileChannel fc = fout.getChannel(); ByteBuffer buffer = ByteBuffer.allocate( 1024 ); for (int i=0; i<message.length; ++i) {
buffer.put( message[i] );
} buffer.flip(); fc.write( buffer ); fout.close();
}

选择器Selector

  可以检测多个NIO channel,看看读或者写事件是否就绪。多个Channel以事件的方式可以注册到同一个Selector,从而达到用一个线程处理多个请求成为可能。

  使用NIO中非阻塞IO编写服务器处理程序,有三个步骤

    1.向Selector对象注册感兴趣的事件

    2.从Selector中获取感兴趣的事件

    3.根据不同事件进行相应的处理

简单API介绍

  open:创建selector
       selectKeys:获取可用channel的集合
       select:选择就绪的通道

简单聊天室实现思路代码

服务器代码

 public class NioServer {

     public void start() throws Exception {
/**
* 1.创建selector
*/
Selector selector = Selector.open();
/**
* 2.通过ServerSocketChannel创建channel
*/
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); /**
* 3.为channel通道绑定监听端口
*/
serverSocketChannel.bind(new InetSocketAddress(8000));
/**
* 4.设置channel 为非阻塞模式
*/
serverSocketChannel.configureBlocking(false);
/**
* 5.将channel 注册到selector,监听连接
*/
serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
System.out.println("服务器启动成功");
/**
* 6.循环等待新接入的连接
*/
for(;;){
int select = selector.select();
if (select == 0){
continue;
} /**
* 7.获取可用channel的集合
*/
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> iterator = keys.iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = (SelectionKey) iterator.next();
/**
* 8.移除selctionKey
*/
iterator.remove();
/**
* 处理具体业务逻辑
*/
/**
* 接入事件
*/
if (selectionKey.isAcceptable()){
acceptHandler(serverSocketChannel,selector);
}
/**
* 可读事件
*/
if(selectionKey.isReadable()){
readHandler(selectionKey, selector);
}
} } /**
* 根据就绪状态,调用对应方法处理业务逻辑
*/ } /**
* 接入事件处理器
*/
private void acceptHandler(ServerSocketChannel serverSocketChannel, Selector selector) throws Exception {
/**
* 如果是接入事件 创建serverSocket
*/
SocketChannel socketChannel = serverSocketChannel.accept();
/**
* 设置非阻塞
*/
socketChannel.configureBlocking(false);
/**
* 注册进selector中
*/
socketChannel.register(selector, SelectionKey.OP_READ);
/**
* 回复服务端信息
*/
socketChannel.write(Charset.forName("UTF-8").encode("你与聊天室里其他人都不是朋友关系,请注意隐私安全")); } private void readHandler(SelectionKey selectionKey, Selector selector) throws Exception{
/**
* 要用selectionKey中获取已经就绪的channel
*/
SocketChannel channel = (SocketChannel)selectionKey.channel();
/**
* 创建buffer
*/
ByteBuffer buffer = ByteBuffer.allocate(1024);
/**
* 循环读取客户端数据
*/
String request = "";
while (channel.read(buffer) > 0){
/**
* 切换读模式
*/
buffer.flip();
/**
* 读取buffer中的内容
*/
request += Charset.forName("UTF-8").decode(buffer); }
/**
* 讲channel注册到selector上
*/
channel.register(selector, SelectionKey.OP_READ);
/**
* 讲客户端发送的请求信息,广播给其他客户端
*/
if (request.length() > 0){
broadCast(selector, channel, request);
}
} private void broadCast(Selector selector, SocketChannel socketChannel, String request){
/**
* 获取到已接入的客户端hannel
*/
Set<SelectionKey> selectionKeys = selector.keys();
selectionKeys.forEach(selectionKey -> {
Channel channel = selectionKey.channel();
if (channel instanceof SocketChannel &&
channel != socketChannel){
try {
//将信息发送到channel客户端
((SocketChannel) channel).write(Charset.forName("UTF-8").encode(request));
} catch (IOException e) {
e.printStackTrace();
}
}
});
/**
* 循环向所有channel广播信息
*/
}
/**
*
* @param args
*/
public static void main(String[] args) throws Exception {
NioServer server = new NioServer();
server.start();
}
}

客户端代码

 public class NioClient {

     public void start() throws Exception {
/**
* 连接服务器
*/
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8000));
/**
* 接收服务器地址
*/
Selector selector = Selector.open();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
new Thread(new NioClientHandler(selector)).start();
/**
* 向服务器发送数据
*/
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()){
String next = scanner.nextLine();
if (StringUtils.isNotBlank(next)){
socketChannel.write(Charset.forName("UTF-8").encode(next));
}
}
} public static void main(String[] args) throws Exception {
new NioClient().start();
}
}

客户端线程类

 public class NioClientHandler implements Runnable {

     private Selector selector;

     public NioClientHandler(Selector selector) {
this.selector = selector;
} @Override
public void run() {
/**
* 循环等待新接入的连接
*/
try {
for(;;){
int select = 0;
select = selector.select(); if (select == 0){
continue;
} /**
* 获取可用channel的集合
*/
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> iterator = keys.iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = (SelectionKey) iterator.next();
/**
* 移除selctionKey
*/
iterator.remove();
/**
* 可读事件
*/
if(selectionKey.isReadable()){
readHandler(selectionKey, selector);
}
}
}
} catch (Exception e) {
e.printStackTrace();
} } private void readHandler(SelectionKey selectionKey, Selector selector) throws Exception{
/**
* 要用selectionKey中获取已经就绪的channel
*/
SocketChannel channel = (SocketChannel)selectionKey.channel();
/**
* 创建buffer
*/
ByteBuffer buffer = ByteBuffer.allocate(1024);
/**
* 循环读取客户端数据
*/
String request = "";
while (channel.read(buffer) > 0){
/**
* 切换读模式
*/
buffer.flip();
/**
* 读取buffer中的内容
*/
request += Charset.forName("UTF-8").decode(buffer); }
/**
* 讲channel注册到selector上
*/
channel.register(selector, SelectionKey.OP_READ);
/**
* 讲客户端发送的请求信息,广播给其他客户端
*/
if (request.length() > 0){
System.out.println(request);
}
}
}

 

Nio学习笔记(大部分网上摘抄)的更多相关文章

  1. Java NIO学习笔记

    Java NIO学习笔记 一 基本概念 IO 是主存和外部设备 ( 硬盘.终端和网络等 ) 拷贝数据的过程. IO 是操作系统的底层功能实现,底层通过 I/O 指令进行完成. 所有语言运行时系统提供执 ...

  2. 零拷贝详解 Java NIO学习笔记四(零拷贝详解)

    转 https://blog.csdn.net/u013096088/article/details/79122671 Java NIO学习笔记四(零拷贝详解) 2018年01月21日 20:20:5 ...

  3. Java NIO 学习笔记(七)----NIO/IO 的对比和总结

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  4. Java NIO 学习笔记(六)----异步文件通道 AsynchronousFileChannel

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  5. Java NIO 学习笔记(五)----路径、文件和管道 Path/Files/Pipe

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  6. Java NIO 学习笔记(四)----文件通道和网络通道

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  7. Java NIO 学习笔记(三)----Selector

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  8. Java NIO 学习笔记(二)----聚集和分散,通道到通道

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  9. Java NIO 学习笔记(一)----概述,Channel/Buffer

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

随机推荐

  1. web容量规划

    容量和性能   容量规划是基于当前性能判断系统需要什么及什么时候需要,它既是资本支出合理化证明过程也是一个技术变更;   性能调优是优化已存在的系统性能;   一般服务的升级步骤是:性能调优 -> ...

  2. [Vue warn]: Avoid using non-primitive value as key

    <el-select v-model="addform.province" placeholder="请选择省份" multiple>        ...

  3. receipt

    receipt - 必应词典 美[riˈsiːt]英[rɪ'siːt] n.收据:收入:接受:字据 v.开收据 网络收到:收条:发票 变形复数:receipts: 搭配give receipt:sig ...

  4. win10安装Navicat 12 for MySQL

    Navicat 下载地址: https://blog.csdn.net/u013600314/article/details/80605981 Navicat 连接Mysql 的方法:https:// ...

  5. Diamond types are not supported at language level '5‘

    当时,我问了下大神,他们问我是不是jdk问题.因为jdk8才支持这样的棱形写法.当时自己的jdk版本是jdk8,然后就奇怪了,最后我发现原来在Language level中调成了5.0 5.0不支持6 ...

  6. https://github.com/python/cpython/blob/master/Doc/library/contextlib.rst 被同一个线程多次获取的同步基元组件

    # -*- coding: utf-8 -*- import time from threading import Lock, RLock from datetime import datetime ...

  7. To avoid slowing down lookups on a near-full table, we resize the table when it's USABLE_FRACTION (currently two-thirds) full.

    https://github.com/python/cpython/blob/3.8/Objects/dictobject.c

  8. could not find 'gopls

    安装go tools 安装以上后用vim打开go代码,使用函数跳转时会出现: E718: Funcref requiredvim-go: could not find 'gopls'. Run :Go ...

  9. 搭建 Kafka 集群 (v2.12-2.3.0)

    服务器:10.20.32.121,10.20.32.122,10.20.32.123 三台服务器都需要安装jdk.配置zookeeper.配置kafka 1.安装配置jdk1.8 [root@loca ...

  10. Microsoft VBScript 运行时错误 错误 800a005e 无效使用 Null: Replace

    查看数据库   表的字段里面是否有空的字段. where 字段名 is null