Java I/O(一) NIO概述
基本概念
- BIO:是堵塞I/O,无论是磁盘I/O,还是网络I/O,数据在写入OutputStream和InputStream都可能发生堵塞,一旦有堵塞,线程会失去CPU的使用权(堵塞)。
- NIO:简单的说就是非堵塞式I/O(单个I/O堵塞时不堵塞线程),一个线程能够负责多个I/O连接(利用serverSocketChannels来接收),取一个准备好接收数据的连接(注冊到Selector轮询调度)。尽快地用连接向缓存区(利用buffer优化I/O)填充数据,然后转向下一个准备好的连接。
- 缓存区(buffer):通信通道(channel)用来数据传输的介质,在NOI模型中,不再通向输出流写入数据或从输入流读取数据。而是在缓存区中读写数据,能够有效降低I/O中断次数,调优I/O。(我会写博客专门讲缓存区的…链接留个位置)
- 通道(channel):负责将缓冲区的数据块移入或移出到各种I/O源(我会写博客专门讲通道的…链接留个位置)
- 就绪选择(selector):为完毕就绪选择。要将不同的通道注冊到一个Selector对象。每一个通道分配有一个SeletionKey。
然后程序能够通过询问Selector对象。得知哪些通道已经准备就绪。能够无堵塞地完毕I/O操作,能够向Selector对象查询对应的键集合。
通道和I/O流的差别
- 流和通道间的关键差别是流是基于字节的,而通道是基于块的。块的单次中断传输的数据量远大于字节,所以性能是有优势。当然,出于性能考虑,流也能够传输字节数组。
- 通常情况下(如网络通道)。通道的缓冲区支持单个通道的读写,而流是仅仅读或者仅仅写的。CDROM是仅仅读通道,但这仅仅是特例
理解I/O:性能的瓶颈
- 现代计算机基于冯诺依曼的存储运行模型,所以数据在计算机部件间的传输速率决定了计算机的运行效率。可是各级存储(CPU寄存器、缓存、内存、硬盘)的传输速度差异巨大(数量级上的差距),在传统的BIO模式下,这导致快速计算部件的大量时间浪费在等待数据传输上。然而计算机作为节点的计算机网络中。问题依然存在。
- 传输速度上:
CPU>内存>磁盘>网络(一般情况下)
- 传统的做法是通过缓存和多线程解决这一问题:
- 快速缓存能够降低中断次数(单次传输的数据量增大,数据总量不变)和中断的时间(缓存一般比原始存储位置传输速率快)
- 多线程能够让单个线程处理一个I/O,不会影响线程获得CPU资源。可是当I/O连接数量增多时,线程的数量随之添加,生成多个线程以及线程之间切换的开销是不容忽略的,线程管理的开销(时间+资源)极大地降低了系统性能
- 借鉴多线程对于I/O的解决方式,我们进一步解决的就是优化掉创建线程和线程间切换带来的巨大的开销,那么也就是採用单个线程非堵塞地管理多个I/O连接,也就是我们要讲的NIO。
例讲Channel和buffer的简单使用
枯燥的解说API没有意义,也太没意思了。所以我採取更加直观的样例来说明API。不严谨但更简单直观。首先为了方便測试我们利用Channel写的Client。我们先用简单的几行代码实现一个本地的測试server:
package com.company;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TestServer {
public static void main(String[] args){
//server监听请求的端口
int port = 9999;
ServerSocket server=null;
try {
server = new ServerSocket(port);
}catch( IOException e ){
System.out.println("server创建失败");
e.printStackTrace();
}
Socket temp=null;
try{
temp = server.accept();
}catch( IOException e ){
System.out.println("获取连接失败");
e.printStackTrace();
}
OutputStream output = null;
try{
output = temp.getOutputStream();
}catch ( Exception e ){
e.printStackTrace();
}
byte [] buffer = "llin of Tianjin University love JAVA and Alibaba!".getBytes();
while ( true ) {
try {
output.write(buffer);
} catch (IOException e) {
e.printStackTrace();
}
try {
//防止传输信息过于快,不方便我们測试
Thread.sleep(1000);
}catch ( InterruptedException e ){
e.printStackTrace();
}
}
}
}
主要作用是间隔一段时间向客户端发送一段信息,用来測试客户端的channel是否实际发挥了作用
以下是一个样例,能够用来简单熟悉一下JAVA中的Buffer和Channel接口的使用,尽管实际中这样使用的意义不大
package com.company;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.SocketChannel;
import java.nio.channels.WritableByteChannel;
/**
* Created by liulin on 16-4-12.
*/
public class TestClient {
//默认的IP
public static final String ipAddress = "127.0.0.1";
//默认端口
public static final int DEFAULT_PORT = 9999;
//缓存的大小
public static final int BUFFER_SIZE = 200;
public static void main ( String [] args ){
if ( args.length == 0 ){//假设没有给出IP地址那么没办法找到測试server啊
System.out.println( "请输入server的IP地址或域名");
}
//给定端口那么使用指定端口,否则使用默认端口
int port=0;
try{
port = Integer.parseInt(args[1]);
}catch ( Exception e ){
port = DEFAULT_PORT;
e.printStackTrace();
}
try{//本例仅仅是让大家熟悉JAVA提供的Buffer,Channel的API。让不熟悉Socket的同学学习下Socket机制
//所以不採用非堵塞机制,也不使用selector
SocketAddress address = new InetSocketAddress( ipAddress , port );
//通过静态工厂获得指定address的通道实例
SocketChannel client = SocketChannel.open(address);
//通过静态工厂获得指定大小的缓存
ByteBuffer buffer = ByteBuffer.allocate( BUFFER_SIZE );
WritableByteChannel out = Channels.newChannel( System.out );//输出通道
//从server读取数据
while ( client.read(buffer) != -1 ){
//将缓存的position置为buffer内置数组的初始位置,当前position位置设置为limit
//position,limit,capacity的概念会在专门介绍buffer的博客中一齐解说
//简单地讲,就是告诉buffer。我们要開始从头读取/改动你了
buffer.flip();
out.write( buffer );
//为了方便查看调试信息,输出一个换行
System.out.write( "\n".getBytes());
//仅仅是改动position为buffer内置数组初始位置。limit设置为capacity
//眼下能够简单地理解为清空缓存(详情我会在专门介绍buffer的博客中一齐解说)
buffer.clear();
}
}catch ( Exception e ){
e.printStackTrace();
}
}
}
主要就是利用Channel连接到server而且通过buffer进行读写。输出到控制台的一个简单的样例,运行效果例如以下(測试的时候一定要先打开測试server):
用一个NIO的HTTPserver解说NIO
作为一个有着TDD思维的程序猴子。怎么能不先写測试用的客户端呢….事实上仅仅是无耻地在之前客户端添了一行代码
package com.company;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.SocketChannel;
import java.nio.channels.WritableByteChannel;
/**
* Created by liulin on 16-4-12.
*/
public class TestClient {
//默认的IP
public static final String ipAddress = "127.0.0.1";
//默认端口
public static final int DEFAULT_PORT = 9999;
//缓存的大小
public static final int BUFFER_SIZE = 200;
public static void main ( String [] args ){
if ( args.length == 0 ){//假设没有给出IP地址那么没办法找到測试server啊
System.out.println( "请输入server的IP地址或域名");
}
//给定端口那么使用指定端口,否则使用默认端口
int port=0;
try{
port = Integer.parseInt(args[1]);
}catch ( Exception e ){
port = DEFAULT_PORT;
e.printStackTrace();
}
try{//本例仅仅是让大家熟悉JAVA提供的Buffer,Channel的API。让不熟悉Socket的同学学习下Socket机制
//所以不採用非堵塞机制,也不使用selector
SocketAddress address = new InetSocketAddress( ipAddress , port );
//通过静态工厂获得指定address的通道实例
SocketChannel client = SocketChannel.open(address);
//通过静态工厂获得指定大小的缓存
ByteBuffer buffer = ByteBuffer.allocate( BUFFER_SIZE );
WritableByteChannel out = Channels.newChannel( System.out );//输出通道
//从server读取数据
client.write( ByteBuffer.wrap("testing...".getBytes()));
while ( client.read(buffer) != -1 ){
//将缓存的position置为buffer内置数组的初始位置,当前position位置设置为limit
//position,limit,capacity的概念会在专门介绍buffer的博客中一齐解说
//简单地讲。就是告诉buffer。我们要開始从头读取/改动你了
buffer.flip();
out.write( buffer );
//为了方便查看调试信息。输出一个换行
System.out.write( "\n".getBytes());
//仅仅是改动position为buffer内置数组初始位置。limit设置为capacity
//眼下能够简单地理解为清空缓存(详情我会在专门介绍buffer的博客中一齐解说)
buffer.clear();
}
}catch ( Exception e ){
e.printStackTrace();
}
}
}
以下是由selector调度的HTTPserver,解说写在了凝视中,假设还有不明确的能够在评论中问
package com.company;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.SocketChannel;
import java.nio.channels.WritableByteChannel;
/**
* Created by liulin on 16-4-12.
*/
public class TestClient {
//默认的IP
public static final String ipAddress = "127.0.0.1";
//默认端口
public static final int DEFAULT_PORT = 9999;
//缓存的大小
public static final int BUFFER_SIZE = 200;
public static void main ( String [] args ){
if ( args.length == 0 ){//假设没有给出IP地址那么没办法找到測试server啊
System.out.println( "请输入server的IP地址或域名");
}
//给定端口那么使用指定端口,否则使用默认端口
int port=0;
try{
port = Integer.parseInt(args[1]);
}catch ( Exception e ){
port = DEFAULT_PORT;
e.printStackTrace();
}
try{//本例仅仅是让大家熟悉JAVA提供的Buffer,Channel的API,让不熟悉Socket的同学学习下Socket机制
//所以不採用非堵塞机制,也不使用selector
SocketAddress address = new InetSocketAddress( ipAddress , port );
//通过静态工厂获得指定address的通道实例
SocketChannel client = SocketChannel.open(address);
//通过静态工厂获得指定大小的缓存
ByteBuffer buffer = ByteBuffer.allocate( BUFFER_SIZE );
WritableByteChannel out = Channels.newChannel( System.out );//输出通道
//从server读取数据
client.write( ByteBuffer.wrap("testing...".getBytes()));
while ( client.read(buffer) != -1 ){
//将缓存的position置为buffer内置数组的初始位置,当前position位置设置为limit
//position,limit,capacity的概念会在专门介绍buffer的博客中一齐解说
//简单地讲,就是告诉buffer,我们要開始从头读取/改动你了
buffer.flip();
out.write( buffer );
//为了方便查看调试信息,输出一个换行
System.out.write( "\n".getBytes());
//仅仅是改动position为buffer内置数组初始位置。limit设置为capacity
//眼下能够简单地理解为清空缓存(详情我会在专门介绍buffer的博客中一齐解说)
buffer.clear();
}
}catch ( Exception e ){
e.printStackTrace();
}
}
}
測试结果例如以下:
详细的细节,我还会在新的博客里详谈,这篇篇幅太长了
參考书目
Java网络编程(第三版)
深入分析Java Web技术内幕
Java I/O(一) NIO概述的更多相关文章
- Java NIO:NIO概述
Java NIO:NIO概述 在上一篇博文中讲述了几种IO模型,现在我们开始进入Java NIO编程主题.NIO是Java 4里面提供的新的API,目的是用来解决传统IO的问题.本文下面分别从Java ...
- (转载)Java NIO:NIO概述(一)
Java NIO:NIO概述 在上一篇博文中讲述了几种IO模型,现在我们开始进入Java NIO编程主题.NIO是Java 4里面提供的新的API,目的是用来解决传统IO的问题.本文下面分别从Java ...
- Java NIO学习笔记一 Java NIO概述
Java NIO概述 Java NIO(新的IO)是Java的替代IO API(来自Java 1.4),这意味着替代标准的 java IO和java Networking API.Java NIO提供 ...
- Java NIO系列教程(一) Java NIO 概述
<I/O模型之四:Java 浅析I/O模型> 一.阻塞IO与非阻塞IO 阻塞IO: 通常在进行同步I/O操作时,如果读取数据,代码会阻塞直至有 可供读取的数据.同样,写入调用将会阻塞直至数 ...
- Java NIO2:NIO概述
一.概述 从JDK1.4开始,Java提供了一系列改进的输入/输出处理的新特性,被统称为NIO(即New I/O).新增了许多用于处理输入输出的类,这些类都被放在java.nio包及子包下,并且对原j ...
- Java NIO学习笔记---I/O与NIO概述
文章目录: 1.什么是IO 2.什么是Java NIO 3.I/O常见概念 4.为什么使用NIO 5.IO VS NIO 一.什么是IO I/O 或者输入/输出 , 指的是计算机与外部世界或者一个程序 ...
- Java网络编程和NIO详解9:基于NIO的网络编程框架Netty
Java网络编程和NIO详解9:基于NIO的网络编程框架Netty 转自https://sylvanassun.github.io/2017/11/30/2017-11-30-netty_introd ...
- Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理
Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理 转自:https://www.jianshu.com/p/2b71ea919d49 本系列文章首发于我的个人博 ...
- Java I/O and NIO [reproduced]
Java I/O and NIO.2---Five ways to maximize Java NIO and NIO.2---Build more responsive Java applicati ...
随机推荐
- oracle 创建表空间及oracle 11g表空间之最大最小
/*分为四步 *//*第1步:创建临时表空间 */create temporary tablespace emaoyi_temp tempfile 'D:\app\Administrator\prod ...
- 1.2 Activity
Activity是个应用组件,它给用户提供了为了完成某些工作而可以进行交互操作的界面,例如,电话详情,打电 话,发邮件,或是浏览地图.每一个Activity都有一个窗口来绘制自已的用户界面.通常来说, ...
- django 模板报错
"Requested setting TEMPLATE_DEBUG, but settings are not configured. You must either define the ...
- traceroute/tracert--获取网络路由路径
traceroute 是用来检测发出数据包的主机到目标主机之间所经过的网关数量的工具.traceroute 的原理是试图以最小的TTL发出探测包来跟踪数据包到达目标主机所经过的网关,然后监听一个来自网 ...
- php安装redis扩展初始化失败解决办法
错误信息如下: PHP Warning: PHP Startup: redis: Unable to initialize module Module compiled with module API ...
- centos7.2 安装 Elasticsearch5.2
打算上全文检索,就找到了找个产品,开始研究下…… 1.官网地址: https://www.elastic.co/guide/en/elasticsearch/reference/5.2/install ...
- 第二百零五节,jQuery EasyUI,Messager(消息窗口)组件
jQuery EasyUI,Messager(消息窗口)组件 学习要点: 1.加载方式 2.属性列表 3.方法列表 本节课重点了解 EasyUI 中 Messager(消息窗口)组件的使用方法,这个组 ...
- 说出几个与spring同类型的开源框架,说出几个与hibernate同类型的开源框架,说出几个与struts同类型的开源框架
说出几个与spring同类型的开源框架,说出几个与hibernate同类型的开源框架,说出几个与struts同类型的开源框架 解答: 1)与spring同类型的开源框架:JUIDE.EJB3.0.pi ...
- SQLAllocStmt与SQLFreeStmt
1.申请语句句柄 SQLAllocStmt函数为应用程序分配语句句柄,其格式为:RETCODE SQLAllocStmt(HDBC hdbc, HSTMT FAR * phstmt) 其中, hdbc ...
- IntelliJ Idea Hide excluded folders 隐藏或显示你需要的文件夹
IntelliJ Idea,以前用idea时,经常maven编译就出现了exclude下的文件夹通常是target,如何隐藏自己不想看见的文件夹,或显示偶尔会用到的文件夹 点击齿轮右下小标 选中文件夹 ...