原文地址:http://www.javaworld.com/article/2078654/java-se/java-se-five-ways-to-maximize-java-nio-and-nio-2.html

Java NIO -- the New Input/Output API package-- was introduced with J2SE 1.4 in 2002. Java NIO's purpose was to improve the programming of I/O-intensive chores on the Java platform. A decade later, many Java programmers still don't know how to make the best use of NIO, and even fewer are aware that Java SE 7 introduced More New Input/Output APIs (NIO.2). In this tutorial you'll find five easy examples that demonstrate the advantages of the NIO and NIO.2 packages in common Java programming scenarios.

 

The primary contribution of NIO and NIO.2 to the Java platform is to improve performance in one of the core areas of Java application development: input/output processing. Neither package is particularly easy to work with, nor are the New Input/Output APIs required for every Java I/O scenario. Used correctly, though, Java NIO and NIO.2 can slash the time required for some common I/O operations. That's the superpower of NIO and NIO.2, and this article presents five relatively simple ways to leverage it:

  1. Change notifiers (because everybody needs a listener)
  2. Selectors help multiplex
  3. Channels -- promise and reality
  4. Memory mapping -- where it counts
  5. Character encoding and searching

The NIO context

How is it that a 10-year-old enhancement is still the NewInput/Output package for Java? The reason is that for many working Java programmers the basic Java I/O operations are more than adequate. Most Java developers don't have to learn NIO for our daily work. Moreover, NIO isn't just a performance package. Instead, it's a heterogeneous collection of facilities related to Java I/O. NIO boosts Java application performance by getting "closer to the metal" of a Java program, meaning that the NIO and NIO.2 APIs expose lower-level-system operating-system (OS) entry points. The tradeoff of NIO is that it simultaneously gives us greater control over I/O and demands that we exercise more care than we would with basic I/O programming. Another aspect of NIO is its attention to application expressivity, which we'll play with in some of the exercises that follow.

Starting out with NIO and NIO.2

Plenty of good references are available for NIO -- see Resources for some selected links. For starting out with NIO and NIO.2, the Java 2 SDK Standard Edition (SE) documentation and Java SE 7 documentation are indispensable. In order to run the examples in this article you will need to be working with JDK 7 or greater.

For many developers the first encounter with NIO might happen during maintenance work: an application has correct functionality but is slow to respond, so someone suggests using NIO to accelerate it. NIO shines when it's used to boost processing performance, but its results will be closely tied to the underlying platform. (Note that NIO is platform dependent.) If you're using NIO for the first time, it will pay you to measure carefully. You might discover that NIO's ability to accelerate application performance depends not only on the OS, but on the specific JVM, host virtualization context, mass storage characteristics, and even data. Measurement can be tricky to generalize, however. Keep this in mind particularly if a mobile deployment is among your targets.

 

And now, without further ado, let's explore five important facilities of NIO and NIO.2.

1. Change notifiers (because everybody needs a listener)

Java application performance is the common draw for developers interested in NIO or NIO.2. In my experience, however, NIO.2's file change notifier is the most compelling (if under-sung) feature of the New Input/Output APIs.

Many enterprise-class applications need to take a specific action when:

  • A file is uploaded into an FTP folder
  • A configuration definition is changed
  • A draft document is updated
  • Another file-system event occurs

These are all examples of change notification or change response. In early versions of Java (and other languages), polling was typically the best way to detect change events. Polling is a particular kind of endless loop: check the file-system or other object, compare it to its last-known state, and, if there's no change, check back again after a brief interval, such as a hundred milliseconds or ten seconds. Continue the loop indefinitely.

NIO.2 gives us a better way to express change detection. Listing 1 is a simple example.

Listing 1. Change notification in NIO.2

import java.nio.file.attribute.*;
import java.io.*;
import java.util.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.List; public class Watcher {
public static void main(String[] args) {
Path this_dir = Paths.get(".");
System.out.println("Now watching the current directory ..."); try {
WatchService watcher = this_dir.getFileSystem().newWatchService();
this_dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE); WatchKey watckKey = watcher.take(); List<WatchEvent< &64;>> events = watckKey.pollEvents();
for (WatchEvent event : events) {
System.out.println("Someone just created the file '" + event.context().toString() + "'."); } } catch (Exception e) {
System.out.println("Error: " + e.toString());
}
}
}
 

Compile this source, then launch the command-line executable. In the same directory, create a new file; you might, for example, touch example1, or even copy Watcher.class example1. You should see the following change notification message:

Someone just create the file 'example1'.

This simple example illustrates how to begin accessing NIO's language facilities in Java. It also introduces NIO.2'sWatcher class, which is considerably more straightforward and easy-to-use for change notification than the traditional I/O solution based on polling.

Watch for typos!

Be careful when you copy source from this article. Note, for instance, that theStandardWatchEventKinds object in Listing 1 is spelled as a plural. Even theJava.net documentation missed that!

Tip

NIO's notifiers are so much easier to use than the polling loops of old that it's tempting to neglect requirements analysis. But you should think through these semantics the first time you use a listener. Knowing when a file modification ends is more useful than knowing when it begins, for instance. That kind of analysis takes some care, especially in a common case like the FTP drop folder. NIO is a powerful package with some subtle "gotcha's"; it can punish a casual visitor.

2. Selectors and asynchronous I/O: Selectors help multiplex

Newcomers to NIO sometimes associate it with "non-blocking input/output." NIO is more than non-blocking I/O but the error makes sense: basic I/O in Java is blocking -- meaning that it waits until it can complete an operation -- whereas non-blocking, or asynchronous, I/O is one of the most-used NIO facilities.

NIO's non-blocking I/O is event-based, as demonstrated by the file-system listener in Listing 1. This means that a selector (or callback or listener) is defined for an I/O channel, then processing continues. When an event occurs on the selector -- when a line of input arrives, for instance -- the selector "wakes up" and executes. All of this is achieved within a single thread, which is a significant contrast to typical Java I/O.

Listing 2 demonstrates the use of NIO selectors in a multi-port networking echo-er, a program slightly modified from one created by Greg Travis in 2003 (see Resources). Unix and Unix-like operating systems have long had efficient implementations of selectors, so this sort of networking program is a model of good performance for a Java-coded networking program.

Listing 2. NIO selectors

import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.util.*; public class MultiPortEcho
{
private int ports[];
private ByteBuffer echoBuffer = ByteBuffer.allocate( 1024 ); public MultiPortEcho( int ports[] ) throws IOException {
this.ports = ports; configure_selector();
} private void configure_selector() throws IOException {
// Create a new selector
Selector selector = Selector.open(); // Open a listener on each port, and register each one
// with the selector
for (int i=0; i<ports.length; ++i) {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ServerSocket ss = ssc.socket();
InetSocketAddress address = new InetSocketAddress(ports[i]);
ss.bind(address); SelectionKey key = ssc.register(selector, SelectionKey.OP_ACCEPT); System.out.println("Going to listen on " + ports[i]);
} while (true) {
int num = selector.select(); Set selectedKeys = selector.selectedKeys();
Iterator it = selectedKeys.iterator(); while (it.hasNext()) {
SelectionKey key = (SelectionKey) it.next(); if ((key.readyOps() & SelectionKey.OP_ACCEPT)
== SelectionKey.OP_ACCEPT) {
// Accept the new connection
ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
SocketChannel sc = ssc.accept();
sc.configureBlocking(false); // Add the new connection to the selector
SelectionKey newKey = sc.register(selector, SelectionKey.OP_READ);
it.remove(); System.out.println( "Got connection from "+sc );
} else if ((key.readyOps() & SelectionKey.OP_READ)
== SelectionKey.OP_READ) {
// Read the data
SocketChannel sc = (SocketChannel)key.channel(); // Echo data
int bytesEchoed = 0;
while (true) {
echoBuffer.clear(); int number_of_bytes = sc.read(echoBuffer); if (number_of_bytes <= 0) {
break;
} echoBuffer.flip(); sc.write(echoBuffer);
bytesEchoed += number_of_bytes;
} System.out.println("Echoed " + bytesEchoed + " from " + sc); it.remove();
} }
}
} static public void main( String args[] ) throws Exception {
if (args.length<=0){System.err.println("Usage: java MultiPortEcho port [port port ...]");System.exit(1);}int ports[]=newint[args.length];for(int i=0; i<args.length;++i){
ports[i]=Integer.parseInt(args[i]);}newMultiPortEcho(ports);}}
Compile this source, then launch it from the command-line with an invocation such as java MultiPortEcho 8005 8006. Once the MultiPortEchoer is running, start up a simple telnet or other terminal emulator running against ports 8005 and 8006. You will see that the program echoes back characters it receives -- and does it in a single Java thread!

More NIO on JavaWorld

See the following JavaWorld articles for more background on the java.nio package APIs.

3. Channels: Promise and reality

In NIO, a channel can be any object that reads or writes. Its job is to abstract files and sockets. NIO channels support a consistent collection of methods, so it's possible to program without having special cases depending on whether stdout, a network connection, or some other channel is actually in use. Channels share this characteristic of the streams of Java's basic I/O. Streams provide blocking I/O; channels support asynchronous I/O.

While NIO is often promoted for its performance advantages, it's more precise to say it is highly responsive. In some cases NIO actually performs worse than basic Java I/O. For simple sequential reads and writes of small files, for instance, a straightforward streams implementation might be two or three times faster than the corresponding event-oriented channel-based coding. Also, non-multiplexed channels -- that is, channels in separate threads -- can be much slower than channels that register their selectors in a single thread.e you need to define a programming problem in terms of dimensions pertinent to streams or channels, try asking the following questions:

  • How many I/O objects must you read and write?
  • Is there a natural sequence between different I/O objects or might they all need to happen simultaneously?
  • Do your I/O objects only last for a short interval or are they likely to persist during the lifetime of your process?
  • Is it more natural to do your I/O in a single thread or several distinct ones?
  • Is network traffic likely to look the same as local I/O or do the two have different patterns?

This sort of analysis is good practice for deciding when to use streams or channels. Remember: NIO and NIO.2 don't replace basic I/O; they just supplement it.

4. Memory mapping -- where it counts

The most consistently dramatic performance improvement in the use of NIO involves memory mapping. Memory mapping is an OS-level service that makes segments of a file appear for programming purposes like areas of memory.

Memory mapping has a number of consequences and implications, more than I'll get into here. At a high level, it helps make I/O happen at the speed of memory access, rather than file access. The former is often two orders of magnitude faster than the latter. Listing 3 is a minimal demonstration of NIO's memory-mapping facility.

Listing 3. Memory mapping in NIO

  import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel; public class mem_map_example {
private static int mem_map_size = 20 * 1024 * 1024;
private static String fn = "example_memory_mapped_file.txt"; public static void main(String[] args) throws Exception {
RandomAccessFile memoryMappedFile = new RandomAccessFile(fn, "rw"); //Mapping a file into memory
MappedByteBuffer out = memoryMappedFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, mem_map_size); //Writing into Memory Mapped File
for (int i = 0; i < mem_map_size; i++) {
out.put((byte) 'A');
}
System.out.println("File '" + fn + "' is now " + Integer.toString(mem_map_size) + " bytes full."); // Read from memory-mapped file.
for (int i = 0; i < 30 ; i++) {
System.out.print((char) out.get(i));
}
System.out.println("\nReading from memory-mapped file '" + fn + "' is complete.");
}
}

The small model in Listing 3 quickly creates a 20-megabyte file,example_memory_mapped_file.txt, fills the file with the character A, then reads the first 30 bytes of the file. In practical cases, memory mapping is interesting not only for the raw speed of I/O, but also because several different readers and writers can attach simultaneously to the same file image. This technique is powerful enough to be dangerous, but in the right hands it makes for exceedingly fast implementations. Wall Street trading operations notoriously leverage memory mapping in order to gain seconds, or even milliseconds, on competitors.

5. Character encoding and searching

The final feature of NIO that I want to introduce in this article is charset, a package for converting between different character encodings. Even before NIO, Java built in much of the same functionality through the getBytes method. charset is welcome, though, because it is more flexible than getBytes and easier to implement at a low architectural level, which yields superior performance. This is particularly valuable for searches that must be sensitive to the encoding, collation, and other characteristics of languages other than English.

Listing 4 shows an example of a conversion from Java's native Unicode character encoding to Latin-1.

Listing 4. Character encoding in NIO

String some_string = "This is a string that Java natively stores as Unicode.";
Charset latin1_charset = Charset.forName("ISO-8859-1");
CharsetEncode latin1_encoder = charset.newEncoder();
ByteBuffer latin1_bbuf = latin1_encoder.encode(CharBuffer.wrap(some_string));

Note that Charsets and channels are designed to work well together in order to ensure that programs requiring cooperation between memory mapping, asynchronous I/O, and encoding translation perform adequately.

Conclusion: Of course there's more

The purpose of this article has been to familiarize working Java developers with some of the main (and most useful) facilities of NIO and NIO.2. You can use the foundation established by the examples here to understand some of NIO's secondary methods; for instance, what you've learned about channels will help you make sense of NIO's Path facility for managing symbolic file-system links. See the Resourcessection as well, for a listing of more in-depth articles about the Java New Input/Output APIs.

Five ways to maximize Java NIO and NIO.2--转的更多相关文章

  1. Five ways to maximize Java NIO and NIO.2--reference

    Java NIO -- the New Input/Output API package-- was introduced with J2SE 1.4 in 2002. Java NIO's purp ...

  2. Java NIO:NIO概述

    Java NIO:NIO概述 在上一篇博文中讲述了几种IO模型,现在我们开始进入Java NIO编程主题.NIO是Java 4里面提供的新的API,目的是用来解决传统IO的问题.本文下面分别从Java ...

  3. Java NIO、NIO.2学习笔记

    相关学习资料 http://www.molotang.com/articles/903.html http://www.ibm.com/developerworks/cn/education/java ...

  4. Java基础知识强化之IO流笔记72:NIO之 NIO核心组件(NIO使用代码示例)

    1.Java NIO 由以下几个核心部分组成: Channels(通道) Buffers(缓冲区) Selectors(选择器) 虽然Java NIO 中除此之外还有很多类和组件,Channel,Bu ...

  5. Java Socket(3): NIO

    NIO采取通道(Channel)和缓冲区(Buffer)来传输和保存数据,它是非阻塞式的I/O,即在等待连接.读写数据(这些都是在一线程以客户端的程序中会阻塞线程的操作)的时候,程序也可以做其他事情, ...

  6. (转载)Java NIO:NIO概述(一)

    Java NIO:NIO概述 在上一篇博文中讲述了几种IO模型,现在我们开始进入Java NIO编程主题.NIO是Java 4里面提供的新的API,目的是用来解决传统IO的问题.本文下面分别从Java ...

  7. 【JAVA】【NIO】3、Java NIO Channel

    Java NIO和流量相似,但有些差异: ·通道可读写,流仅支持单向.读或写 ·异步通道读取 ·通道读写器,他们是和Buffer交替 道的实现 下面是Java NIO中最重要的通道的实现: ·File ...

  8. 【JAVA】【NIO】5、Java NIO Scatter / Gather

    标题手段Java NIO该分散体浓缩 Java NIO内置支持分散与收集.的概念主要用于信道分散聚集的读写. 读出的分散体的一个通道被读多个数据buffer在.因此.数据分散到多个buffer中. 对 ...

  9. 漫谈Java IO之 NIO那些事儿

    前面一篇中已经介绍了基本IO的使用以及最简单的阻塞服务器的例子,本篇就来介绍下NIO的相关内容,前面的分享可以参考目录: 网络IO的基本知识与概念 普通IO以及BIO服务器 NIO的使用与服务器Hel ...

随机推荐

  1. 隐马尔科夫模型(HMM)

    基本概念 1Markov Models 2Hidden Markov Models 3概率计算算法前向后向算法 1-3-1直接计算 1-3-2前向算法 1-3-3后向算法 4学习问题Baum-Welc ...

  2. Ehcache整合spring配置,配置springMVC缓存

    为了提高系统的运行效率,引入缓存机制,减少数据库访问和磁盘IO.下面说明一下ehcache和spring整合配置. 1.   需要的jar包 slf4j-api-1.6.1.jar ehcache-c ...

  3. Android自定义组件系列【16】——最帅气的自动滚动广告条

    前一段时间要实现一个滚动的广告条,参考了一下网上许多实现,发现实现都很麻烦,所以我决定自己使用ViewFlipper来实现一个,在此将代码贴出来,与大家共享. 转载请说明出处:http://blog. ...

  4. JAVA学习书籍

    1 java编程思想(第四版) 2 head first servlet &jsp

  5. vmware-images

    https://virtualboxes.org/images/centos/ https://www.osboxes.org/vmware-images/

  6. openSUSE Leap与 SELS的区别

    openSUSE Leap 是 openSUSE 常规发行版本的新名称,在 13.2 之前它仅仅被称为“openSUSE”. 一.openSUSE 发行周期:(15年以前仅有一个openSUSE发行版 ...

  7. ping 原理及ICMP协议简介

    //Ping IP/域名 public static String pingIPCennect(String ipStr) {  String result = "";  if ( ...

  8. python多线程理解

    在发送网络请求的过程中,单个请求的速度总是有着很大的限制,而任务往往需要以更快的速度去执行,这时多线程就是一个很好地选择.python已经给我们封装好了多线程库thread和threading. th ...

  9. weex入门(一)

    emmmm其实没有接触过weex ,了解一番发现有很多坑,有很多基于weex改良后的框架,比如weexplus等等,基本不用踩多少坑.经过几天的深思熟虑我觉得还是去踩坑,毕竟踩完坑才能真正的了解嘛 w ...

  10. pwd---以绝对路径的方式显示用户当前工作目录

    pwd命令以绝对路径的方式显示用户当前工作目录.命令将当前目录的全路径名称(从根目录)写入标准输出.全部目录使用/分隔.第一个/表示根目录,最后一个目录是当前目录.执行pwd命令可立刻得知您目前所在的 ...