利不百不变法,功不十不易器
为什么会出现nio,之前的io有什么问题?
请先看 说说nio1
nio类图如下
package io; //: io/GetChannel.java // Getting channels from streams import java.nio.*; import java.nio.channels.*; import java.io.*; public class GetChannel { private static final int BSIZE = 1024; public static void main(String[] args) throws Exception { // Write a file: FileChannel fc = new FileOutputStream("data.txt").getChannel(); fc.write(ByteBuffer.wrap("Some text ".getBytes())); fc.close(); // Add to the end of the file: fc = new RandomAccessFile("data.txt", "rw").getChannel(); fc.position(fc.size()); // Move to the end fc.write(ByteBuffer.wrap("Some more".getBytes())); fc.close(); // Read the file: fc = new FileInputStream("data.txt").getChannel(); ByteBuffer buff = ByteBuffer.allocate(BSIZE); fc.read(buff); buff.flip(); while(buff.hasRemaining()) System.out.print((char)buff.get()); } } /* Output: Some text Some more *///:~
package io; import java.nio.ByteBuffer; import java.nio.channels.*; import java.util.concurrent.*; import java.io.*; public class FileLocking { public static void main(String[] args) throws Exception { FileOutputStream fos= new FileOutputStream("file.txt"); FileLock fl = fos.getChannel().tryLock(); if(fl != null) { System.out.println("Locked File"); FileChannel fc =fos.getChannel(); fc.write(ByteBuffer.wrap("Some textssss".getBytes())); TimeUnit.MILLISECONDS.sleep(1000); fl.release(); System.out.println("Released Lock"); } fos.close(); } } /* Output: Locked File Released Lock *///:~
package io; import java.io.IOException; import java.io.RandomAccessFile; import java.net.URL; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.channels.OverlappingFileLockException; /** * 测试NIO中的文件锁:三个线程争抢文件锁,获得锁后向文件中写数据,然后再释放文件锁。 * * @author aofeng <a href="mailto:[email protected]>[email protected]</a> */ public class LockTest implements Runnable { public void run() { Thread curr = Thread.currentThread(); System.out.println("Current executing thread is " + curr.getName()); URL url = LockTest.class.getClassLoader().getResource("file.txt"); //路径问题 //http://www.cnblogs.com/rongxh7/archive/2010/04/22/1718178.html //http://www.cnblogs.com/yejg1212/p/3270152.html RandomAccessFile raf = null; FileChannel fc = null; FileLock lock = null; try { //就是这里的路径问题, 为什么要替换%20 去上面的资料里看 raf = new RandomAccessFile(url.getPath().replaceAll("%20"," "), "rw"); fc = raf.getChannel(); System.out.println(curr.getName() + " ready"); // 轮流获得文件独占锁。 while (true) { try { lock = fc.lock(); break; } catch (OverlappingFileLockException e) { Thread.sleep(1 * 1000); } } if (null != lock) { System.out.println(curr.getName() + " get filelock success"); fc.position(fc.size()); fc.write(ByteBuffer.wrap((curr.getName() + " write data. ") .getBytes())); } else { System.out.println(curr.getName() + " get filelock fail"); } fc.close(); raf.close(); } catch (Exception e) { e.printStackTrace(); } finally { // 注意:要先释放锁,再关闭通道。 if (null != lock && lock.isValid()) { try { lock.release(); System.out.println(curr.getName() + " release filelock"); } catch (IOException e) { e.printStackTrace(); } } } } /** * @param args */ public static void main(String[] args) { Thread t1 = new Thread(new LockTest()); t1.setName("t1"); Thread t2 = new Thread(new LockTest()); t2.setName("t2"); Thread t3 = new Thread(new LockTest()); t3.setName("t3"); t1.start(); t2.start(); t3.start(); } }
ok现在我们看看在网络中,nio是在怎么运作的
public static void acceptConnections( int port) throws Exception { // 打开一个 Selector Selector acceptSelector = SelectorProvider.provider().openSelector(); // 创建一个 ServerSocketChannel ,这是一个 SelectableChannel 的子类 ServerSocketChannel ssc = ServerSocketChannel.open(); // 将其设为 non-blocking 状态,这样才能进行非阻塞 IO 操作 ssc.configureBlocking( false ); // 给 ServerSocketChannel 对应的 socket 绑定 IP 和端口 InetAddress lh = InetAddress.getLocalHost(); InetSocketAddress isa = new InetSocketAddress(lh, port); ssc.socket().bind(isa); // 将 ServerSocketChannel 注册到 Selector 上,返回对应的 SelectionKey ssc.register(acceptSelector, SelectionKey.OP_ACCEPT); int keysAdded = 0; // 用 select() 函数来监控注册在 Selector 上的 SelectableChannel // 返回值代表了有多少 channel 可以进行 IO 操作 (ready for IO) while ((keysAdded = acceptSelector.select()) > 0) { // selectedKeys() 返回一个 SelectionKey 的集合, // 其中每个 SelectionKey 代表了一个可以进行 IO 操作的 channel 。 // 一个 ServerSocketChannel 可以进行 IO 操作意味着有新的 TCP 连接连入了 Set<SelectionKey> readyKeys = acceptSelector.selectedKeys(); Iterator<SelectionKey> i = readyKeys.iterator(); while (i.hasNext()) { SelectionKey sk = (SelectionKey) i.next(); // 需要将处理过的 key 从 selectedKeys 这个集合中删除 i.remove(); // 从 SelectionKey 得到对应的 channel ServerSocketChannel nextReady =(ServerSocketChannel) sk.channel(); // 接受新的 TCP 连接 Socket s = nextReady.accept().socket(); // 把当前的时间写到这个新的 TCP 连接中 PrintWriter out =new PrintWriter(s.getOutputStream(), true ); Date now = new Date(); out.println(now); // 关闭连接 out.close(); } } }
///////////////////////以下为2016年3月9日修改
<p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; font-size: 14px; line-height: 25.2px;"><span style="font-size:12px;">ava NIO是在jdk1.4开始使用的,它既可以说成“新I/O”,也可以说成非阻塞式I/O。下面是java NIO的工作原理: 1. 由一个专门的线程来处理所有的 IO 事件,并负责分发。 2. 事件驱动机制:事件到的时候触发,而不是同步的去监视事件。 3. 线程通讯:线程之间通过 wait,notify 等方式通讯。保证每次上下文切换都是有意义的。减少无谓的线程切换。 阅读过一些资料之后,下面贴出我理解的java NIO的工作原理图:</span></p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; font-size: 14px; line-height: 25.2px;"> </p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; font-size: 14px; line-height: 25.2px;"><img src="http://dl.iteye.com/upload/attachment/0066/2123/c17e2880-a712-349f-a818-2c921303f224.jpg" width="689" alt="" height="251" style="border: 0px;" /></p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; font-size: 14px; line-height: 25.2px;"> </p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; font-size: 14px; line-height: 25.2px;"><span style="font-size:12px;">(注:每个线程的处理流程大概都是读取数据、解码、计算处理、编码、发送响应。) Java NIO的服务端只需启动一个专门的线程来处理所有的 IO 事件,这种通信模型是怎么实现的呢?呵呵,我们一起来探究它的奥秘吧。java NIO采用了双向通道(channel)进行数据传输,而不是单向的流(stream),在通道上可以注册我们感兴趣的事件。一共有以下四种事件:</span></p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; font-size: 14px; line-height: 25.2px;"> </p><table border="1" style="color: rgb(0, 0, 0); font-family: Helvetica, Tahoma, Arial, sans-serif; font-size: 14px; line-height: 25.2px; height: 109px; width: 432px;"><tbody><tr><td style="font-size: 1em;">事件名</td><td style="font-size: 1em;">对应值</td></tr><tr><td style="font-size: 1em;">服务端接收客户端连接事件</td><td style="font-size: 1em;">SelectionKey.OP_ACCEPT(16)</td></tr><tr><td style="font-size: 1em;">客户端连接服务端事件</td><td style="font-size: 1em;">SelectionKey.OP_CONNECT(8)</td></tr><tr><td style="font-size: 1em;">读事件</td><td style="font-size: 1em;">SelectionKey.OP_READ(1)</td></tr><tr><td style="font-size: 1em;">写事件</td><td style="font-size: 1em;">SelectionKey.OP_WRITE(4)</td></tr></tbody></table><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; font-size: 14px; line-height: 25.2px;"> </p><table border="0.5" style="color: rgb(0, 0, 0); font-family: Helvetica, Tahoma, Arial, sans-serif; font-size: 14px; line-height: 25.2px;"><tbody><tr><td style="font-size: 1em;"> </td><td style="font-size: 1em;"> </td></tr><tr><td style="font-size: 1em;"> </td><td style="font-size: 1em;"> </td></tr><tr><td style="font-size: 1em;"> </td><td style="font-size: 1em;"> </td></tr><tr><td style="font-size: 1em;"> </td><td style="font-size: 1em;"> </td></tr><tr><td style="font-size: 1em;"> </td><td style="font-size: 1em;"><span style="color: rgb(204, 0, 0); font-family: Helvetica, Tahoma, Arial, sans-serif;font-size:12px; line-height: 25.2px;">服务端和客户端各自维护一个管理通道的对象,我们称之为selector,该对象能检测一个或多个通道 (channel) 上的事件。我们以服务端为例,如果服务端的selector上注册了读事件,某时刻客户端给服务端发送了一些数据,阻塞I/O这时会调用read()方法阻塞地读取数据,而NIO的服务端会在selector中添加一个读事件。服务端的处理线程会轮询地访问selector,如果访问selector时发现有感兴趣的事件到达,则处理这些事件,如果没有感兴趣的事件到达,则处理线程会一直阻塞直到感兴趣的事件到达为止。下面是我理解的java NIO的通信模型示意图:</span></td></tr></tbody></table><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; font-size: 14px; line-height: 25.2px;"> </p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; font-size: 14px; line-height: 25.2px;"><img src="http://dl.iteye.com/upload/attachment/0066/3190/0184183e-286c-34f1-9742-4adaa28b7003.jpg" width="700" alt="" height="159" title="点击查看原始大小图片" class="magplus" style="border: 0px; cursor: url("/images/magplus.gif"), pointer;" /></p>
参考资料: Java NIO原理图文分析及代码实现
<pre code_snippet_id="483455" snippet_file_name="blog_20141013_6_1843553" name="code" class="java">///////////////////////以上为2016年3月9日修改
一般来说 I/O 模型可以分为:同步阻塞,同步非阻塞,异步阻塞,异步非阻塞 四种IO模型
同步阻塞 IO : 我们在上一篇文章nio1里面说的就是这个
在此种方式下,用户进程在发起一个 IO 操作以后,必须等待 IO 操作的完成,只有当真正完成了 IO 操作以后,用户进程才能运行。 JAVA传统的 IO 模型属于此种方式!
我去找小明借书 小明开始在自己的家里找 我什么事情都不干 就在他家里等 他找到后给我 这就是同步阻塞
同步非阻塞 IO: 这篇文章说nio就是 指这个
在此种方式下,用户进程发起一个 IO 操作以后 边可 返回做其它事情,但是用户进程需要时不时的询问 IO 操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的 CPU 资源浪费。其中目前 JAVA 的 NIO 就属于同步非阻塞 IO 。
我去找小明借书 小明开始找 我先去玩 过一会来看 如果没有找到 那就继续去玩 等会再来看 找到了 那就一切OK
异步阻塞 IO : 还没有提到
此种方式下是指应用发起一个 IO 操作以后,不等待内核 IO 操作的完成,等内核完成 IO 操作以后会通知应用程序,这其实就是同步和异步最关键的区别,同步必须等待或者主动的去询问 IO 是否完成,那么为什么说是阻塞的呢?因为此时是通过 select 系统调用来完成的,而 select 函数本身的实现方式是阻塞的,而采用 select 函数有个好处就是它可以同时监听多个文件句柄,从而提高系统的并发性!
我去找小明借书 小明开始找 我先去玩 他找到书后 会给我送来
异步非阻塞 IO:
在此种模式下,用户进程只需要发起一个 IO 操作然后立即返回,等 IO 操作真正的完成以后,应用程序会得到 IO 操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的 IO 读写操作,因为 真正的 IO读取或者写入操作已经由 内核完成了。目前 Java 中还没有支持此种 IO 模型。
关于异步非阻塞 IO: 请参考 http://janeky.iteye.com/blog/1073695http://www.blogjava.net/19851985lili/articles/93524.html
http://www.zhihu.com/question/27991975/answer/69041973
深入分析java web内幕 许令波 第二章 深入分析java/io的工作机制
http://janeky.iteye.com/blog/1073695