目录

1.Java NIO基本介绍

2.NIO和BIO的比较

3.NIO三大核心原理示意图

Selector、Channel和Buffer的关系图

关系图的说明

4.缓冲区(Buffer)

基本介绍

Buffer类及其子类

Bytebuffer

5.通道(Channel)

基本介绍

FileChannel类

关于Buffer和Channel的注意事项和细节

6.Selector(选择器)

基本介绍

Selector示意图和特点说明

Selector类相关方法

7.NIO非阻塞网络编程原理图

8.NIO非阻塞网络编程快速入门

9.SelectionKey

10.ServerSocketChannel

11.SocketChannel

12.NIO网络编程应用实例——群聊系统

13.NIO与零拷贝(TODO)

14.Java AIO基本介绍

BIO、NIO、AIO对比表


1.Java NIO基本介绍

Java NIO全称java non-blocking IO,是指JDK提供的新API,从JDK 1.4开始,Java提供了一系列改进

的输入/输出的新特性,被称为NIO,是同步非阻塞的。

NIO相关类都被放在java.nio包下,并且对原java.io包中的很多类进行了改写。

NIO有三大核心部分:Channel(通道)、Buffer(缓冲区)、Selector(选择器)。

NIO是面向缓冲区或者面向块编程的,数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动,这就

增加了处理过程中的灵活性,使用它可以提供非阻塞时的高伸缩性网络。

NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,

如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,所以,直到数据变得可以

读取之前,该线程可以继续做其他事情,非阻塞写也是如此,一个线程请求写入一些数据到某通道,但不需要

等待它完全写入,这个线程同时可以去做别的事情。

通俗理解:NIO是可以做到一个线程来处理多个操作的,假设有10000个请求过来,根据实际

情况,可以分配50或100个线程来处理,不像之前的阻塞IO那样,非得分配10000个。

HTTP2.0使用了多路复用的技术,做到同一个连接并发处理多个请求,并且并发请求的数量比HTTP1.1大了好几个数量级。

2.NIO和BIO的比较

BIO以流的方式处理数据,而NIO以块的方式处理数据,块I/O的效率比流I/O高很多。

BIO是阻塞的,NIO则是非阻塞的。

BIO基于字节流和字符流进行操作,而NIO基于Channel(通道)和Buffer(缓冲区)进行

操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择器)

用于监听多个通道的事件(比如:连接请求、数据到达等),因此使用单个线程就可以监听

多个客户端通道。

3.NIO三大核心原理示意图

Selector、Channel和Buffer的关系图

一张图描述NIO的Selector、Channel和Buffer的关系。

关系图的说明

1)每个Channel都会对应一个Buffer

2)Selector对应一个线程,一个线程对应多个Channel

3)该图反映了有三个Channel注册到该Selector程序

4)程序切换到哪个Channel是由事件决定的,event是一个重要的概念

5)Selector会根据不同的事件,在各个通道上切换

6)Buffer就是一个内存块,底层有一个数组

7)数据的读取写入是通过Buffer,Buffer可以读也可以写,需要flip方法切换

8)Channel是双向的,可以返回底层操作系统的情况,比如Linux底层的操作系统通道就是双向的

4.缓冲区(Buffer)

基本介绍

缓冲区(Buffer):本质上是可以读写数据的内存块(数组),其提供了一些方法,能够跟踪跟踪缓冲区的状态变化。Channel提供从文件、网络读取数据的渠道,但是读取或写入的数据都必须经过缓冲区。

Buffer类及其子类

在NIO中,Buffer是一个顶层父类,它是一个抽象类,其子类包括

  • ByteBuffer(最常用)

  • ShortBuffer

  • CharBuffer

  • IntBuffer

  • LongBuffer

  • DoubleBuffer

  • FloatBuffer

Buffer类有四个属性:

  • capacity:容量,即可以容纳的最大数据量,在缓冲区创建时被设定且不能改变

  • limit:表示缓冲区的当前终点,不能对缓冲区超过极限的位置进行读写操作

  • position:表示下一次要被读或写的元素的索引,每次读写缓冲区数据时都会改变,为下次读写做准备

  • mark:标记

Bytebuffer

最常用的是ByteBuffer类(二进制数据),该类的主要方法如下:

public abstract class ByteBuffer {//缓冲区创建相关apipublic static ByteBuffer allocateDirect(int capacity)//创建直接缓存区public static ByteBuffer allocate(int capacity)public static ByteBuffer wrap(byte[] array)public static ByteBuffer wrap(byte[] array, int offset, in length)//缓存区存取相关apipublic abstract byte get()public abstract byte get(int index)public abstract ByteBuffer put(byte b)public abstract ByteBuffer put(int index, byte b)
}

5.通道(Channel)

基本介绍

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

  • 通道可以同时进行读写,而流只能读或只能写。BIO中stream是单向的,例如FileInputStream只能进行读取数据的操作。而NIO中的channel是双向的,可以读操作,也可以写操作。

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

  • 通道可以从缓冲区读数据,也可以写数据到缓冲区

常用的Channel类:FileChannel、DatagramChannel、ServerSocketChannel、SocketChannel

FileChannel类

FileChannel主要用来对本地文件进行IO操作,常见的方法有

public int read(ByteBuffer dst)//从通道读取数据并放入到缓冲区中
public int write(ByteBuffer src)//把缓冲区的数据写入到通道中
//从目标通道中复制数据到当前通道
public long transferFrom(ReadableByteChannel src, long position, long count)
//把数据从当前通道复制给目标通道
public long transferTo(long position, long count, WritableByteChannel target)

关于Buffer和Channel的注意事项和细节

NIO还提供了MappedByeBuffer,可以让文件直接在内存(堆外内存)中进行修改,而如何同步

到文件由NIO来完成。

NIO还支持通过多个Buffer(即Buffer数组)完成读写操作,即Scattering和Gatering。

6.Selector(选择器)

基本介绍

Java NIO,用非阻塞的方式,可以用同一个线程,处理多个客户端的链接,就会使用到Selector。

Selector能够检测多个注册的通道是否有事件发生,如果有事件发生,便获取事件针对每个事件进行相应的处理,这样就可以只用一个单线程去管理多个通道,也就是管理多个连接和请求。

只有在连接真正有读写事件发生时,才会进行读写,就大大地减少了系统开销,并且不必为

每个连接都创建一个线程,不用去维护多个线程,避免了多个线程之间的上下文切换导致的开销。

Selector示意图和特点说明

特点说明:

1)Selector可以同时并发处理成百上千个客户端连接

2)当线程从某客户端socket通道进行读写数据时,若没有数据可用时,该线程可以进行其他任务

3)线程通常将非阻塞IO的空闲时间用于在其他通道上执行IO操作,所以单独的线程可以管理

多个输入和输出通道

4)由于读写操作都是非阻塞的,这就可以充分提升IO线程的运行效率,避免由于频繁IO阻塞导致的线程挂起

5)一个IO线程可以并发处理N个客户端连接和读写操作,这次根本上解决了传统同步阻塞IO一连接

一线程模型,架构的性能、弹性伸缩能力和可靠性都得到了极大的提升

Selector类相关方法

Selector类是一个抽象类,常用方法和说明如下:

public static Selector open() throws IOException;
public abstract int select() throws IOException; //如果没有事件会一直阻塞
public abstract int selectNow() throws IOException; //非阻塞,立刻返回
public abstract int select(long timeout) throws IOException;//等待一段时间
public abstract Set<SelectionKey> selectedKeys();//有事件发生的SelectionKey的集合
public abstract Selector wakeup();//唤醒 

7.NIO非阻塞网络编程原理图

NIO非阻塞网络编程相关的Selector、ServerSocketChannel、SocketChannel关系图

说明:

1)服务端会有个Selector,

2)当服务端启动时,ServerSocketChannel注册到Selector上,然后Selector进行事件循环,对事件进行响应

2)Selector进行事件循环

  • accept事件:当客户端连接时,会有accept事件产生,ServerSocketChannel通过accept得到SocketChannel,并将SocketChannel注册到Selector上

  • read事件:获取到对应的channel,进行读事件处理

  • write事件:获取到对应的channel,进行写事件处理

8.NIO非阻塞网络编程快速入门

9.SelectionKey

SelectionKey,表示Selector和网络通道的注册关系,共四种:

int OP_ACCEPT:有新的网络连接可以accept,值为16

int OP_CONNECT:代表连接已经建立,值为8

int OP_READ:代表读操作,值为1

int OP_WRITE:代表写操作,值为4

源码中:

    public static final int OP_READ = 1 << 0;public static final int OP_WRITE = 1 << 2;public static final int OP_CONNECT = 1 << 3;public static final int OP_ACCEPT = 1 << 4;

SelectionKey相关方法

public abstract class SelectionKey {public abstract SelectableChannel channel();public abstract Selector selector();public final Object attachment()
​public abstract SelectionKey interestOps(int ops);public final boolean isReadable() public final boolean isWritable()public final boolean isConnectable() public final boolean isAcceptable()
}

10.ServerSocketChannel

ServerSocketChannel在服务器端监听新的客户端连接。

常用方法如下:

public abstract class ServerSocketChannelextends AbstractSelectableChannelimplements NetworkChannel
{public static ServerSocketChannel open()public final ServerSocketChannel bind(SocketAddress local)public abstract SocketChannel accept()public final SelectableChannel configureBlocking(boolean block)public final SelectionKey register(Selector sel, int ops, Object att)
}

11.SocketChannel

SocketChannel,网络IO通道,具体负责进行读写操作,NIO把缓冲区的数据写入通道,或者

把通道里的数据读到缓冲区。

常用方法如下:

public abstract class SocketChannelextends AbstractSelectableChannelimplements ByteChannel, ScatteringByteChannel, GatheringByteChannel, NetworkChannel
{public static SocketChannel open()public boolean connect(SocketAddress remote)public boolean finishConnect()public abstract int write(ByteBuffer src)public abstract int read(ByteBuffer dst)public final SelectableChannel configureBlocking(boolean block)public final SelectionKey register(Selector sel, int ops, Object att)
}

12.NIO网络编程应用实例——群聊系统

目的:理解NIO非阻塞网络编程机制

要求:

1)编写一个NIO群聊系统,实现服务器端和客户端之间的数据简单通讯(非阻塞)

2)实现多人群聊

3)服务器端:可以检测用户上线、离线,并实现消息转发功能

4)客户端:通过channel可以无阻塞发送消息非其他所有用户,同时可以接受其他用户

发送的消息(由服务器转发得到)

服务器端:

package com.thelight1.groupchat;
​
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
​
public class GroupChatServer {
​private Selector selector;private ServerSocketChannel listenChannel;private static final int PORT = 6667;
​public GroupChatServer() {try {selector = Selector.open();listenChannel = ServerSocketChannel.open();listenChannel.socket().bind(new InetSocketAddress(PORT));listenChannel.configureBlocking(false);listenChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("服务器启动了......");} catch (IOException e) {e.printStackTrace();}}
​public void listen() {try {while (true) {int count = selector.select(2000);if (count > 0) {Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()) {SelectionKey key = iterator.next();
​if (key.isAcceptable()) {SocketChannel socketChannel = listenChannel.accept();socketChannel.configureBlocking(false);socketChannel.register(selector, SelectionKey.OP_READ);System.out.println(socketChannel.getRemoteAddress() + " 上线了....");}if (key.isReadable()) {readData(key);}
​iterator.remove();}
​} else {
//                    System.out.println("等待.....");}}} catch (Exception e) {e.printStackTrace();} finally {
​}}
​private void readData(SelectionKey key) {SocketChannel channel = null;try {channel = (SocketChannel)key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);int count = channel.read(buffer);if (count > 0) {String msg = new String(buffer.array());System.out.println("from 客户端:" + msg.trim());sendInfoToOtherClients(msg, channel);}
​} catch (IOException e) {try {System.out.println(channel.getRemoteAddress() + "离线了");//取消注册key.cancel();//关闭通道channel.close();} catch (IOException e1) {e1.printStackTrace();}}}
​private void sendInfoToOtherClients(String msg, SocketChannel self) throws IOException {System.out.println("服务器转发消息中.......");
​for (SelectionKey key : selector.keys()) {Channel channel = key.channel();if (channel instanceof SocketChannel && channel != self) {SocketChannel socketChannel = (SocketChannel)channel;ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());socketChannel.write(buffer);}}}
​public static void main(String[] args) {GroupChatServer groupChatServer = new GroupChatServer();groupChatServer.listen();}
}

客户端:

package com.thelight1.groupchat;
​
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.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;
​
public class GroupChatClient {
​private static final String HOST = "127.0.0.1";private static final int PORT = 6667;private Selector selector;private SocketChannel socketChannel;private String username;
​public GroupChatClient() throws IOException {selector = Selector.open();socketChannel = SocketChannel.open(new InetSocketAddress(HOST, PORT));socketChannel.configureBlocking(false);socketChannel.register(selector, SelectionKey.OP_READ);
​username = socketChannel.getLocalAddress().toString().substring(1);System.out.println(username + "is ok");}
​public void sendInfo(String info) {info = username + "说:" + info;
​try {socketChannel.write(ByteBuffer.wrap(info.getBytes()));} catch (IOException e) {e.printStackTrace();}}
​public void readInfo() {try {int readChannel = selector.select();if (readChannel > 0) {Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()) {SelectionKey key = iterator.next();if (key.isReadable()) {SocketChannel channel = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);int count = channel.read(buffer);if (count > 0) {String msg = new String(buffer.array());System.out.println(msg.trim());}}iterator.remove();}}} catch (IOException e) {e.printStackTrace();}}
​public static void main(String[] args) throws IOException {final GroupChatClient groupChatClient = new GroupChatClient();Thread thread = new Thread(new Runnable() {@Overridepublic void run() {while (true) {groupChatClient.readInfo();try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}}
​}});thread.setDaemon(true);thread.start();
​Scanner scanner = new Scanner(System.in);while (scanner.hasNextLine()) {String str = scanner.nextLine();groupChatClient.sendInfo(str);}}
}

13.NIO与零拷贝(TODO)

14.Java AIO基本介绍

JDK7引入了Asynchronous I/O,即AIO,在进行I/O编程中,常用到两种模式:Reactor和Proactor。

Java的NIO就是Reactor,当有事件触发时,服务器端得到通知,进行相应的处理。

AIO即NIO2.0,也叫异步不阻塞的IO。AIO引入异步通道的概念,采用了Proactor模式,简化了程序编写,有效地请求才启动线程, 它的特点是先由操作系统完成后才通知服务器端程序启动线程去处理,一般适用于连接数较多且连接时间较长的应用。

目前AIO还没有广泛使用,Netty也是基于NIO,而不是AIO。

有兴趣的可以参考《Java新一代网络编程模型AIO原理及Linux系统AIO介绍》http://www.52im.net/thread-306-1-1.html

BIO、NIO、AIO对比表

举例说明:

同步阻塞:到理发店理发,就一直等理发师,知道轮到自己理发

同步非阻塞:到理发店理发,发现前面有其他人理发,给理发师说下,先干其他事情,一会过来看是否

轮到自己

异步非阻塞:给理发师打电话,让理发师上门服务,自己干其他事情,理发师来你家给你理发

第3章-Java NIO编程相关推荐

  1. JAVA NIO编程入门(二)

    一.回顾 上一篇文章 JAVA NIO编程入门(一)我们学习了NIO编程的基础知识,并通过一个小demo实战帮助了解NIO编程的channel,buffer等概念.本文会继续学习JAVA NIO编程, ...

  2. Java NIO编程的技巧和陷阱

    去年做的分享,一直上传slideshare失败,今天又试了下,成功了.这个主题主要介绍Java NIO编程的技巧和陷阱,解读了一些NIO框架的源码,以及编写高性能NIO网络框架所需要注意的技巧和缺陷. ...

  3. Java NIO编程基础

    Java NIO编程基础 Java NIO 基本介绍 NIO的Buffer基本使用 NIO 和 BIO 的比较 NIO 三大核心原理示意图 缓冲区(Buffer) 基本介绍 Buffer 类及其子类 ...

  4. 第十一章 Java IO编程

    第十一章 Java IO编程 11.1 文件操作类:File java.io包中,如果要进行文件自身操作(创建,删除),只能依靠java.io.File类完成. NO. 方法 类型 描述 1 publ ...

  5. Java NIO 编程:Buffer、Channel、Selector原理详解

    1 Java 中的 I/O模型:BIO.NIO.AIO 1.1 BIO.NIO.AIO概念介绍 I/O 模型简单的理解:就是 用什么样的通道进行数据的发送和接收,很大程度上决定了程序通信的性能. Ja ...

  6. Java NIO编程

    NIO 同步非阻塞的编程方式 主要是解决BIO的大并发问题,NIO最重要的地方是当一个连接创建后,对应一个线程,这个连接会被注册到多路复用器上面,所以所有的连接只需要一个线程就可以完成,当这个线程中的 ...

  7. 三、Java NIO 编程

    3.1.Java NIO 基本介绍 Java NIO 全称java non-blocking IO,是指JDK提供的新API.从JDK1.4开始,Java提供了一系列改进的输入/输出的新特性,被统称为 ...

  8. Java并发编程 - 第十一章 Java并发编程实践

    前言: 当你在进行并发编程时,看着程序的执行速度在自己的优化下运行得越来越快,你会觉得越来越有成就感,这就是并发编程的魅力.但与此同时,并发编程产生的问题和风险可能也会随之而来.本章先介绍几个并发编程 ...

  9. 第一章 java nio三大组件与使用姿势

    本案例来源于<netty权威指南> 一.三大组件 Selector:多路复用器.轮询注册在其上的Channel,当发现某个或者多个Channel处于"就绪状态"后(ac ...

最新文章

  1. 蓝色梦想,再次起航 | 水下目标检测算法比赛正式开赛!
  2. java 日期和字符串互转,根据当天整天时间 得到当天最后一秒的日期时间
  3. 戴尔推免费浏览器安全工具 可隔离恶意软件
  4. hashMap怎么解决hash冲突的
  5. Zookeeper核心工作机制(zookeeper特性、zookeeper数据结构、节点类型)
  6. ps4修改服务器地区,ps4怎么修改服务器地址
  7. kuka机器人焊接飞溅大_库卡机器人KCP4示教器屏幕校准方法
  8. 同包类 和 其他类 java_关于继承:为什么Java中的“protected”修饰符允许访问同一个包中的其他类?...
  9. lcd像素点密度_【教程】设置添加LCD密度(DPI)设置
  10. Python魔术世界 1 如何使用Visual Studio在WIN10中一键安装Python3入门编程环境并测试Django...
  11. Oracle提升查询性能之-简单范围分区表的创建
  12. 计算机毕业论文java毕业设计成品源码网站基于SSM实现的仓库管理系统
  13. 如何把iphone 6s通讯录导入到诺基亚E72i内
  14. Electron如何修改图标
  15. js限制input的输入字符的长度,区分中英文
  16. 我用Python实现自动化办公,美女同事投来羡慕的眼神,而后···
  17. 3万字BI系统整体建设解决方案
  18. 成为一名合格的软件测试工程师,需要具备哪些技能?
  19. 2022 年 Java 将何去何从?
  20. Graphite监控上手指南

热门文章

  1. USnews 国外排行榜 爬取数据
  2. 专访韶音科技:抓住大湾区机遇 打造世界级耳机品牌
  3. 善用IDM站点管理功能
  4. 转 RabbitMQ 基础概念及 Spring 的配置和使用 推荐好文 举例讲解
  5. 神经网络的参数优化方法
  6. python factorial_Python  factorial
  7. java factorial_[Java]函数求阶乘n!(factorial)(四种方法)
  8. Android与IOS加固的五种方式
  9. 基于Web的Markdown编辑器HedgeDoc
  10. 《Boland传奇》