对于Java NIO,总是看见别人写,使用Java NIO能够提高性能,比BIO的性能要好挺多,但是一直未能深入的研究,不太清楚NIO到底是怎么来提高性能的,Non-blocking到底体现在哪里。这几天搜索了一个,找到一些讲的比较好的文章,并实际写了一个小的程序来理解一下,对NIO有了更进一步的理解。
所参考查询的资料如下:
1. JAVA NIO 简介
http://www.iteye.com/topic/834447
帖子讲解了NIO相关的知识,比较好的比较和总结了BIO和NIO的区别,指出了为什么NIO的性能比BIO要好,解答了我一直的疑问。帖子后面的回复非常有用,一定要看。
2. 使用Java NIO编写高性能的服务器
http://tenyears.iteye.com/blog/40489
代码非常好,通过这个代码可以更好的理解NIO
下面根据2里面的代码写了一个程序,客户端想服务器请求下载文件,如果文件比较大,比较耗时,采用BIO的方式的话,如果起100个线程,只能100个client进行下载,第101个客户就得等待。而且CPU需要不断的切换来知道哪个线程中的IO读写可以进行了,开销比较大。使用NIO后,如果有IO读写到来,服务器就会得到相关的事件,开始进行读写,这样的开销比BIO要小很多,通过一个线程轮询事件就能完成。
看一下代码,里面加了一些log,有助于理解NIO
Server端的代码为:
package com.jyj.test.server;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Iterator;
import java.util.Set;
public class NIOServer {
private static final int BLOCK_SIZE = 4096;
private Selector selector;
private String file = "D:\\Learning\\Java\\HowTomcatWorksApps.zip";
private ByteBuffer buffer = ByteBuffer.allocate(BLOCK_SIZE);
private CharsetDecoder charsetDecoder;
public NIOServer(int port) throws IOException {
selector = this.getSelector(port);
Charset charset = Charset.forName("UTF-8");
charsetDecoder = charset.newDecoder();
}
private Selector getSelector(int port) throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
Selector selector = Selector.open();
serverSocketChannel.socket().bind(new InetSocketAddress(port));
serverSocketChannel.configureBlocking(false);
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
return selector;
}
public void listen() {
while(true) {
try {
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> it = selectionKeys.iterator();
System.out.println("keyset size : " + selectionKeys.size());
while (it.hasNext()) {
SelectionKey selectionKey = it.next();
it.remove();
handleKey(selectionKey);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private void handleKey(SelectionKey key) throws IOException {
if (key.isAcceptable()) {
System.out.println("Accept");
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
SocketChannel channel = serverSocketChannel.accept();
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
SocketChannel channel = (SocketChannel) key.channel();
int count = channel.read(buffer);
if (count > 0) {
buffer.flip();
CharBuffer charBuffer = charsetDecoder.decode(buffer);
String clientName = charBuffer.toString();
System.out.println("Read From Client : " + clientName);
SelectionKey selectionKey = channel.register(selector, SelectionKey.OP_WRITE);
selectionKey.attach(new HandleClient(clientName));
} else {
channel.close();
}
buffer.clear();
} else if (key.isWritable()) {
SocketChannel channel = (SocketChannel) key.channel();
HandleClient handleClient = (HandleClient) key.attachment();
ByteBuffer buffer = handleClient.readBlock();
System.out.println("Write to client : " + handleClient.getClentName());
if (buffer != null) {
channel.write(buffer);
} else {
handleClient.close();
channel.close();
}
}
}
private class HandleClient {
private FileChannel fileChannel;
private ByteBuffer byteBuffer;
private String clientName;
public HandleClient(String clientName) throws FileNotFoundException {
fileChannel = new FileInputStream(file).getChannel();
byteBuffer = ByteBuffer.allocate(BLOCK_SIZE);
this.clientName = clientName;
}
public ByteBuffer readBlock() {
try {
byteBuffer.clear();
int count = fileChannel.read(byteBuffer);
byteBuffer.flip();
if (count < 0) {
return null;
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return byteBuffer;
}
public void close() {
try {
fileChannel.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public String getClentName() {
return clientName;
}
}
public static void main(String [] args) {
int port = 12345;
try {
NIOServer server = new NIOServer(port);
System.out.println("Listening on : " + port);
while (true) {
server.listen();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
client端的代码为:
package com.jyj.test.client;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class NIOClient {
static int SIZE = 10;
static InetSocketAddress address = new InetSocketAddress("localhost", 12345);
static CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();
static class DownloadFile implements Runnable {
int index;
public DownloadFile(int index) {
this.index = index;
}
@Override
public void run() {
long start = System.currentTimeMillis();
try {
SocketChannel client = SocketChannel.open();
client.configureBlocking(false);
Selector selector = Selector.open();
client.register(selector, SelectionKey.OP_CONNECT);
client.connect(address);
ByteBuffer buffer = ByteBuffer.allocate(8 * 1024);
int total = 0;
while(true) {
boolean isExit = false;
selector.select();
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey selectionKey = (SelectionKey) it.next();
it.remove();
if (selectionKey.isConnectable()) {
SocketChannel channel = (SocketChannel) selectionKey.channel();
if (channel.isConnectionPending()) {
channel.finishConnect();
}
channel.write(encoder.encode(CharBuffer.wrap("Hello From " + index)));
channel.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) {
SocketChannel channel = (SocketChannel) selectionKey.channel();
int count = channel.read(buffer);
if (count > 0) {
total += count;
buffer.clear();
} else {
channel.close();
isExit = true;
break;
}
}
}
if (isExit) {
break;
}
}
double last = (System.currentTimeMillis() - start) * 1.0 / 1000;
System.out.println("Thread " + index + " downloaded " + total + " bytes in " + last + "seconds.");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String [] args) {
ExecutorService pool = Executors.newFixedThreadPool(SIZE);
for (int i = 0; i < SIZE; i++) {
pool.execute(new DownloadFile(i));
}
pool.shutdown();
}
}
server端的输出结果如下:
Listening on : 12345
keyset size : 1
Accept
keyset size : 1
Accept
keyset size : 1
Read From Client : Hello From 8
keyset size : 3
Accept
Read From Client : Hello From 2
Write to client : Hello From 8
keyset size : 3
Read From Client : Hello From 5
Write to client : Hello From 2
Write to client : Hello From 8
keyset size : 3
Write to client : Hello From 5
Write to client : Hello From 2
Write to client : Hello From 8
。。。。。。。。。。。。。。。
keyset size : 10
Write to client : Hello From 7
Write to client : Hello From 1
Write to client : Hello From 5
Write to client : Hello From 3
Write to client : Hello From 9
Write to client : Hello From 2
Read From Client : Hello From 0
Write to client : Hello From 6
Write to client : Hello From 8
Write to client : Hello From 4
keyset size : 10
Write to client : Hello From 7
Write to client : Hello From 1
Write to client : Hello From 5
Write to client : Hello From 3
Write to client : Hello From 9
Write to client : Hello From 0
Write to client : Hello From 2
Write to client : Hello From 6
Write to client : Hello From 8
Write to client : Hello From 4
。。。。。。。。。。。。。。。
keyset size : 3
Write to client : Hello From 0
Write to client : Hello From 6
Write to client : Hello From 4
keyset size : 3
Write to client : Hello From 0
Write to client : Hello From 6
Write to client : Hello From 4
keyset size : 3
Write to client : Hello From 0
Write to client : Hello From 6
Write to client : Hello From 4
keyset size : 3
Write to client : Hello From 0
Write to client : Hello From 6
Write to client : Hello From 4
keyset size : 3
Write to client : Hello From 0
Write to client : Hello From 6
Write to client : Hello From 4
keyset size : 2
Write to client : Hello From 6
Write to client : Hello From 4
keyset size : 2
Write to client : Hello From 6
Write to client : Hello From 4
keyset size : 2
Write to client : Hello From 6
Write to client : Hello From 4
keyset size : 1
Write to client : Hello From 4
keyset size : 1
Write to client : Hello From 4
keyset size : 1
Write to client : Hello From 4
keyset size : 1
Write to client : Hello From 4
keyset size : 1
Write to client : Hello From 4
keyset size : 1
Write to client : Hello From 4
client端的输出结果为:
Thread 5 downloaded 3844845 bytes in 1.938seconds.
Thread 2 downloaded 3844845 bytes in 1.953seconds.
Thread 1 downloaded 3844845 bytes in 1.953seconds.
Thread 8 downloaded 3844845 bytes in 1.922seconds.
Thread 9 downloaded 3844845 bytes in 1.922seconds.
Thread 7 downloaded 3844845 bytes in 1.953seconds.
Thread 3 downloaded 3844845 bytes in 1.969seconds.
Thread 0 downloaded 3844845 bytes in 1.969seconds.
Thread 6 downloaded 3844845 bytes in 1.953seconds.
Thread 4 downloaded 3844845 bytes in 1.953seconds.
最近建议找一下linux或者windows的socket编程方面的书看一下,能够更好的理解IO方面的知识,包括Java 7中的AIO。
分享到:
相关推荐
一个java NIO的例子 有很详细的每一步的描述,很好去理解
nio对于java程序员来说可能不是很好理解,但是对于C程序员来说,就是epoll的一个封装。 我本人是C程序员,对java比较感兴趣,发现java nio里面很多的东西都是对C原生api的封装,如epoll, mmap等 要是想学习 java ...
可以作为NIO socket入门的例子,Reactor模式,重点理解key.attach, jar文件里包含了源代码 1,运行server.bat启动服务器,可以打开编辑,修改端口号 2,运行client.bat启动客户端,可以打开编辑,ip,和端口号 3...
Java NIO的总结, 对于新人入门理解很好, 使用Markdown编写
自己总结的java中NIO的笔记,绘制了详细的思维导图,每个思维导图中均有详细的博文解释,方便大家学习和理解,免费分享给大家。适合java的爱好者和学习者
Anontion、Applet、NIO等Demo,可以辅助理解一下相关知识点
一站式学习Java网络编程 全面理解BIO:NIO:AIO1
Netty是基于Java NIO的网络应用框架,如果你是Java网络方面的新手,那么本章将是你学习... 在本章的结尾,你会明白什么是Netty以及Netty提供了什么,你会理解Java NIO和异步处理机制,并通过本书的其他章节加强理解
NIO框架,使得其具有很好的扩展性,也便于理解Java NIO。 一、预备知识 JavaNIO JavaNIO网上已有许多不错的文章和教程供开发者学习,如,等等,详细内容本文不再累述,这里只简要总结一下。 普通IO,也叫BIO、...
Java NIO(New IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API。本系列教程将有助于你学习和理解Java NIO。
谈谈NIO的理解Java系列2021.pdf
深入Hotspot源码与Linux内核理解NIO与Netty线程模型
小型简单但完整的Java NIO服务器,任何人都可以免费使用。 目前,它仅处理发送和接收字符串,并且尚未进行优化-但它易于理解并适应您的需求。
主要介绍了java NIO之Selector(选择器)的相关资料,文中讲解非常详细,实例代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
nio-ssh 当前状态 1-尚不可用 概述 该库是SSH协议(客户端和服务器)的纯... 通过拥有清晰简洁的文档(包括有关所有方法的JavaDoc,甚至包括私有方法),我们希望使该库易于理解和实现。 通过具有极高的单元测试覆盖
尚硅谷java教程全程跟听,手动整理,从面向对象开始按章节按课时整理,适合对照视频作为笔试使用/java知识脉络梳理/八股理解背诵
在进入Java NIO编程之前,我们先来讨论一些比较基础的知识:I/O模型。下面本文先从同步和异步的概念 说起,然后接着阐述了阻塞和非阻塞的区别,接着介绍了阻塞IO和非阻塞IO的区别,然后介绍了同步IO和异步IO的区别,...
NULL 博文链接:https://chinaestone.iteye.com/blog/468138
主要介绍了JAVA NIO的的相关资料,文中讲解非常细致,帮助大家更好的理解和学习,感兴趣的朋友可以了解下
为了最大程度地从这里的讨论中获益,您应该理解基本的 Java 编程概念,如类、继承和使用包。多少熟悉一些原来的 I/O 库(来自 java.io.* 包)也会有所帮助。 虽然本教程要求掌握 Java 语言的工作词汇和概念,但是不...