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. threejs行星运动小demo总结

    1.动画构思 就是中间有个红太阳,外面有几个行星球体环绕着太阳在各自轨道上做圆周运动.下面是效果图 2.基本要素 使用threejs的基本构件包括:渲染器(renderer),相机(camera),场 ...

  2. Bootstrap select 多选并获取选中的值

    代码: <!DOCTYPE html><html> <head>    <meta charset="UTF-8">    < ...

  3. STP生成树算法

    生成树算法第一:决定谁是“根网桥”对比各个网桥ID,先对比ID中的优先级,优先级相同的时候对比网桥MAC地址,对比依据是谁的值最小,谁是“根网桥” 第二:决定哪些是“根端口”窍门——每个非根网桥上都有 ...

  4. jquery 遍历对象、数组、集合

    <div id="result" style="font-size:16px;color:red;"></div><table c ...

  5. C图形化第一步

    之前的贪吃蛇都是在cmd下实现,每次都要调用cls刷新屏幕,简直是闪瞎了我的狗眼. 度娘得知有一种方法可以避免闪烁,即:双缓冲.原理是先在内存中作图,然后将做好的图复制到前台,同时禁止背景刷新. 主要 ...

  6. 手把手教你如何使用量产工具修复u盘

    u盘是我们平时都使用到的一个小工具,我们会使用它来进行储存一些重要的文件,但要是我们操作不当的话,那么就会导致u盘出现一些问题的哟,比如说插入u盘无法打开文件等等,那么遇到这个问题时该这么办呢?那么, ...

  7. 【maven】在pom.xml中引入对json-lib的依赖dependency

    <dependency> <groupId>net.sf.json-lib</groupId> <artifactId>json-lib</art ...

  8. vim 显示行号 临时&永久

    设置vim 永久显示行号 - electrocrazy的博客 - CSDN博客https://blog.csdn.net/electrocrazy/article/details/79035216 v ...

  9. ISO/IEC 9899:2011 条款6.5.8——关系操作符

    6.5.8 关系操作符 语法 1.relational-expression: shift-expression relational-expression    <    shift-expr ...

  10. 【435】NULL '\0' 0 等在 C 语言中的区别

    参考:C/C++语言中NULL.'\0’和0的区别 参考:空字符串.'\0'.0与NULL的区别以及数组清零的特点分析 在 C语言 中没有 空字符 这个东西 '',不过有 空字符串 "&qu ...